diff options
Diffstat (limited to 'libjava/classpath/gnu/java')
129 files changed, 9416 insertions, 1885 deletions
diff --git a/libjava/classpath/gnu/java/awt/ComponentReshapeEvent.java b/libjava/classpath/gnu/java/awt/ComponentReshapeEvent.java new file mode 100644 index 00000000000..8f15c851933 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/ComponentReshapeEvent.java @@ -0,0 +1,85 @@ +/* WindowResizeEvent.java -- Used to synchronize the AWT and peer sizes + 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; + +import java.awt.AWTEvent; +import java.awt.Component; + +/** + * This is used to update the AWT's knowledge about a Window's size when + * the user changes the window bounds. + * + * This event is _not_ posted to the eventqueue, but rather dispatched directly + * via Window.dispatchEvent(). It is the cleanest way we could find to update + * the AWT's knowledge of the window size. Small testprograms showed the + * following: + * - Component.reshape() and its derivatives are _not_ called. This makes sense + * as it could end up in loops,because this calls back into the peers. + * - Intercepting event dispatching for any events in + * EventQueue.dispatchEvent() showed that the size is still updated. So it + * is not done via an event dispatched over the eventqueue. + * + * Possible other candidates for implementation would have been: + * - Call a (private) callback method in Window/Component from the native + * side. + * - Call a (private) callback method in Window/Component via reflection. + * + * Both is uglier than sending this event directly. Note however that this + * is impossible to test, as Component.dispatchEvent() is final and can't be + * intercepted from outside code. But this impossibility to test the issue from + * outside code also means that this shouldn't raise any compatibility issues. + */ +public class ComponentReshapeEvent + extends AWTEvent +{ + + public int x; + public int y; + public int width; + public int height; + + public ComponentReshapeEvent(Component c, int x, int y, int width, int height) + { + super(c, 1999); + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } +} diff --git a/libjava/classpath/gnu/java/awt/LowPriorityEvent.java b/libjava/classpath/gnu/java/awt/LowPriorityEvent.java new file mode 100644 index 00000000000..c1558f6ff58 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/LowPriorityEvent.java @@ -0,0 +1,48 @@ +/* LowPriorityEvent.java -- Marks events with low priority + 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; + +/** + * A marker interface that marks events with low priority. LowPriority events + * are dispatched _after_ other (normal priority) events by the EventQueue. + */ +public interface LowPriorityEvent +{ + // Empty marker interface. +} diff --git a/libjava/classpath/gnu/java/awt/color/PyccConverter.java b/libjava/classpath/gnu/java/awt/color/PyccConverter.java index cd50d8776cd..77ea28a3e92 100644 --- a/libjava/classpath/gnu/java/awt/color/PyccConverter.java +++ b/libjava/classpath/gnu/java/awt/color/PyccConverter.java @@ -37,7 +37,6 @@ exception statement from your version. */ package gnu.java.awt.color; - /** * PyccConverter - conversion routines for the PhotoYCC colorspace * @@ -52,21 +51,21 @@ public class PyccConverter implements ColorSpaceConverter { public float[] toRGB(float[] in) { - return null; + throw new UnsupportedOperationException(); } public float[] fromRGB(float[] in) { - return null; + throw new UnsupportedOperationException(); } public float[] toCIEXYZ(float[] in) { - return null; + throw new UnsupportedOperationException(); } public float[] fromCIEXYZ(float[] in) { - return null; + throw new UnsupportedOperationException(); } } diff --git a/libjava/classpath/gnu/java/awt/dnd/peer/gtk/GtkDragSourceContextPeer.java b/libjava/classpath/gnu/java/awt/dnd/peer/gtk/GtkDragSourceContextPeer.java index 4f922982273..b68fa105803 100644 --- a/libjava/classpath/gnu/java/awt/dnd/peer/gtk/GtkDragSourceContextPeer.java +++ b/libjava/classpath/gnu/java/awt/dnd/peer/gtk/GtkDragSourceContextPeer.java @@ -61,11 +61,13 @@ public class GtkDragSourceContextPeer private ComponentPeer peer; private Cursor cursor; private DragSourceContext context; + public static Component target; native void nativeStartDrag(Image i, int x, int y, int action, String target); native void connectSignals(ComponentPeer comp); native void create(ComponentPeer comp); native void nativeSetCursor(int cursor); + native void setTarget(GtkDropTargetContextPeer target); public GtkDragSourceContextPeer(DragGestureEvent e) { @@ -76,10 +78,18 @@ public class GtkDragSourceContextPeer create(peer); connectSignals(peer); cursor = comp.getCursor(); + + // FIXME: Where do we set the target? + + if ((target != null)) + setTarget(new GtkDropTargetContextPeer(target)); } ComponentPeer getComponentPeer(Component c) { + if (c == null) + return null; + Component curr = c; while (curr.getPeer() instanceof LightweightPeer) curr = curr.getParent(); @@ -93,7 +103,7 @@ public class GtkDragSourceContextPeer throws InvalidDnDOperationException { this.context = context; - + if (p == null) p = new Point(); diff --git a/libjava/classpath/gnu/java/awt/dnd/peer/gtk/GtkDropTargetContextPeer.java b/libjava/classpath/gnu/java/awt/dnd/peer/gtk/GtkDropTargetContextPeer.java index 50cd95d41ad..f24b3f39bcb 100644 --- a/libjava/classpath/gnu/java/awt/dnd/peer/gtk/GtkDropTargetContextPeer.java +++ b/libjava/classpath/gnu/java/awt/dnd/peer/gtk/GtkDropTargetContextPeer.java @@ -50,10 +50,10 @@ public class GtkDropTargetContextPeer extends GtkGenericPeer implements DropTargetContextPeer { - - public GtkDropTargetContextPeer() + + public GtkDropTargetContextPeer(Object obj) { - super(null); + super(obj); } public void setTargetActions(int actions) diff --git a/libjava/classpath/gnu/java/awt/font/autofit/AxisHints.java b/libjava/classpath/gnu/java/awt/font/autofit/AxisHints.java new file mode 100644 index 00000000000..b2c9912342b --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/AxisHints.java @@ -0,0 +1,45 @@ +/* AxisHints.java -- FIXME: briefly describe file purpose + 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.autofit; + +class AxisHints +{ + + Segment[] segments; +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/Constants.java b/libjava/classpath/gnu/java/awt/font/autofit/Constants.java new file mode 100644 index 00000000000..cb3992825ab --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/Constants.java @@ -0,0 +1,61 @@ +/* Constants.java -- Some constants used in the autofitter + 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.autofit; + +/** + * Some constants used in the autofitter. + */ +interface Constants +{ + + /** + * The horizontal dimension. + */ + static final int DIMENSION_HORZ = 0; + + /** + * The vertical dimension. + */ + static final int DIMENSION_VERT = 1; + + /** + * The number of dimensions. + */ + static final int DIMENSION_MAX = 2; +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/GlyphHints.java b/libjava/classpath/gnu/java/awt/font/autofit/GlyphHints.java new file mode 100644 index 00000000000..ad73a04a69b --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/GlyphHints.java @@ -0,0 +1,75 @@ +/* GlyphHints.java -- Data and methods for actual hinting + 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.autofit; + +import gnu.java.awt.font.opentype.truetype.Zone; + +/** + * The data and methods used for the actual hinting process. + */ +class GlyphHints +{ + + int xScale; + int xDelta; + int yScale; + int yDelta; + + AxisHints[] axis; + + void rescale(ScriptMetrics metrics) + { + // TODO: Implement. + } + + void reload(Zone outline) + { + // TODO: Implement. + } + + void computeSegments(int dim) + { + // TODO: Implement. + } + + void linkSegments(int dim) + { + // TODO: Implement. + } +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/Latin.java b/libjava/classpath/gnu/java/awt/font/autofit/Latin.java new file mode 100644 index 00000000000..0352b41a45a --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/Latin.java @@ -0,0 +1,177 @@ +/* Latin.java -- Latin specific glyph handling + 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.autofit; + +import java.awt.geom.AffineTransform; + +import gnu.java.awt.font.opentype.OpenTypeFont; +import gnu.java.awt.font.opentype.truetype.Zone; + +/** + * Implements Latin specific glyph handling. + */ +class Latin + implements Script, Constants +{ + + private static final int MAX_WIDTHS = 16; + + public void applyHints(GlyphHints hints, ScriptMetrics metrics) + { + // TODO Auto-generated method stub + + } + + public void doneMetrics(ScriptMetrics metrics) + { + // TODO Auto-generated method stub + + } + + /** + * Initializes the <code>hints</code> object. + * + * @param hints the hints to initialize + * @param metrics the metrics to use + */ + public void initHints(GlyphHints hints, ScriptMetrics metrics) + { + hints.rescale(metrics); + LatinMetrics lm = (LatinMetrics) metrics; + hints.xScale = lm.axis[DIMENSION_HORZ].scale; + hints.xDelta = lm.axis[DIMENSION_HORZ].delta; + hints.yScale = lm.axis[DIMENSION_VERT].scale; + hints.yDelta = lm.axis[DIMENSION_VERT].delta; + // TODO: Set the scaler and other flags. + } + + /** + * Initializes the script metrics. + * + * @param metrics the script metrics to initialize + * @param face the font + */ + public void initMetrics(ScriptMetrics metrics, OpenTypeFont face) + { + assert metrics instanceof LatinMetrics; + LatinMetrics lm = (LatinMetrics) metrics; + lm.unitsPerEm = face.unitsPerEm; + + // TODO: Check for latin charmap. + + initWidths(lm, face, 'o'); + initBlues(lm, face); + } + + public void scaleMetrics(ScriptMetrics metrics) + { + // TODO Auto-generated method stub + + } + + /** + * Determines the standard stem widths. + * + * @param metrics the metrics to use + * @param face the font face + * @param ch the character that is used for getting the widths + */ + private void initWidths(LatinMetrics metrics, OpenTypeFont face, char ch) + { + GlyphHints hints = new GlyphHints(); + metrics.axis[DIMENSION_HORZ].widthCount = 0; + metrics.axis[DIMENSION_VERT].widthCount = 0; + int glyphIndex = face.getGlyph(ch); + // TODO: Avoid that AffineTransform constructor and change + // getRawGlyphOutline() to accept null or remove that parameter altogether. + // Consider this when the thing is done and we know what we need that for. + Zone outline = face.getRawGlyphOutline(glyphIndex, new AffineTransform()); + LatinMetrics dummy = new LatinMetrics(); + Scaler scaler = dummy.scaler; + dummy.unitsPerEm = metrics.unitsPerEm; + scaler.xScale = scaler.yScale = 10000; + scaler.xDelta = scaler.yDelta = 0; + scaler.face = face; + hints.rescale(dummy); + hints.reload(outline); + for (int dim = 0; dim < DIMENSION_MAX; dim++) + { + LatinAxis axis = metrics.axis[dim]; + AxisHints axHints = hints.axis[dim]; + int numWidths = 0; + hints.computeSegments(dim); + hints.linkSegments(dim); + Segment[] segs = axHints.segments; + for (int i = 0; i < segs.length; i++) + { + Segment seg = segs[i]; + Segment link = seg.link; + if (link != null && link.link == seg && link.index > i) + { + int dist = Math.abs(seg.pos - link.pos); + if (numWidths < MAX_WIDTHS) + axis.widths[numWidths++].org = dist; + } + } + } + for (int dim = 0; dim < DIMENSION_MAX; dim++) + { + LatinAxis axis = metrics.axis[dim]; + int stdw = axis.widthCount > 0 ? axis.widths[0].org + : constant(metrics, 50); + axis.edgeDistanceTreshold= stdw / 5; + } + } + + /** + * Initializes the blue zones of the font. + * + * @param metrics the metrics to use + * @param face the font face to analyze + */ + private void initBlues(LatinMetrics metrics, OpenTypeFont face) + { + // TODO: Implement. + } + + private int constant(LatinMetrics metrics, int c) + { + return c * (metrics.unitsPerEm / 2048); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/LatinAxis.java b/libjava/classpath/gnu/java/awt/font/autofit/LatinAxis.java new file mode 100644 index 00000000000..8ca1e6d9ed2 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/LatinAxis.java @@ -0,0 +1,53 @@ +/* LatinAxis.java -- Axis specific data + 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.autofit; + +/** + * Some axis specific data. + */ +class LatinAxis +{ + + int scale; + int delta; + + int widthCount; + Width[] widths; + float edgeDistanceTreshold; +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/LatinMetrics.java b/libjava/classpath/gnu/java/awt/font/autofit/LatinMetrics.java new file mode 100644 index 00000000000..cd955348b37 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/LatinMetrics.java @@ -0,0 +1,51 @@ +/* LatinMetrics.java -- Latin specific metrics data + 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.autofit; + +/** + * Latin specific metrics data. + */ +class LatinMetrics + extends ScriptMetrics +{ + + LatinAxis[] axis; + + int unitsPerEm; +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/Scaler.java b/libjava/classpath/gnu/java/awt/font/autofit/Scaler.java new file mode 100644 index 00000000000..10518512578 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/Scaler.java @@ -0,0 +1,52 @@ +/* Scaler.java -- FIXME: briefly describe file purpose + 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.autofit; + +import gnu.java.awt.font.opentype.OpenTypeFont; + +class Scaler +{ + + int xScale; + int xDelta; + int yScale; + int yDelta; + OpenTypeFont face; + +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/Script.java b/libjava/classpath/gnu/java/awt/font/autofit/Script.java new file mode 100644 index 00000000000..3b353010f2d --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/Script.java @@ -0,0 +1,62 @@ +/* Script.java -- Defines script specific interface to the autofitter + 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.autofit; + +import gnu.java.awt.font.opentype.OpenTypeFont; + +/** + * Defines script specific methods for the auto fitter. + */ +interface Script +{ + + /** + * Initializes the metrics. + */ + void initMetrics(ScriptMetrics metrics, OpenTypeFont face); + + void scaleMetrics(ScriptMetrics metrics/* , scaler, map this */); + + void doneMetrics(ScriptMetrics metrics); + + void initHints(GlyphHints hints, ScriptMetrics metrics); + + void applyHints(GlyphHints hints, /* some outline object, */ + ScriptMetrics metrics); +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/ScriptMetrics.java b/libjava/classpath/gnu/java/awt/font/autofit/ScriptMetrics.java new file mode 100644 index 00000000000..77c815ae59d --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/ScriptMetrics.java @@ -0,0 +1,49 @@ +/* ScriptMetrics.java -- Script specific metrics data + 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.autofit; + +/** + * Script specific metrics data. + */ +class ScriptMetrics +{ + + Script script; + Scaler scaler; +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/Segment.java b/libjava/classpath/gnu/java/awt/font/autofit/Segment.java new file mode 100644 index 00000000000..32032a48fcc --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/Segment.java @@ -0,0 +1,47 @@ +/* Segment.java -- FIXME: briefly describe file purpose + 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.autofit; + +class Segment +{ + + Segment link; + int index; + int pos; +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/Width.java b/libjava/classpath/gnu/java/awt/font/autofit/Width.java new file mode 100644 index 00000000000..d4d540069ee --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/Width.java @@ -0,0 +1,46 @@ +/* Width.java -- FIXME: briefly describe file purpose + 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.autofit; + +public class Width +{ + int org; + int cur; + int fit; +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/CharGlyphMap.java b/libjava/classpath/gnu/java/awt/font/opentype/CharGlyphMap.java index 6ada3b147c6..184075094b6 100644 --- a/libjava/classpath/gnu/java/awt/font/opentype/CharGlyphMap.java +++ b/libjava/classpath/gnu/java/awt/font/opentype/CharGlyphMap.java @@ -61,7 +61,7 @@ import java.nio.IntBuffer; * * @author Sascha Brawer (brawer@dandelis.ch) */ -abstract class CharGlyphMap +public abstract class CharGlyphMap { private static final int PLATFORM_UNICODE = 0; private static final int PLATFORM_MACINTOSH = 1; diff --git a/libjava/classpath/gnu/java/awt/font/opentype/OpenTypeFont.java b/libjava/classpath/gnu/java/awt/font/opentype/OpenTypeFont.java index 9ee28d76bb4..efc30811f7b 100644 --- a/libjava/classpath/gnu/java/awt/font/opentype/OpenTypeFont.java +++ b/libjava/classpath/gnu/java/awt/font/opentype/OpenTypeFont.java @@ -52,6 +52,7 @@ import java.util.Locale; import gnu.java.awt.font.FontDelegate; import gnu.java.awt.font.GNUGlyphVector; import gnu.java.awt.font.opentype.truetype.TrueTypeScaler; +import gnu.java.awt.font.opentype.truetype.Zone; /** @@ -117,7 +118,7 @@ public final class OpenTypeFont * OpenType fonts with PostScript outlines, other values are * acceptable (such as 1000). */ - private int unitsPerEm; + public int unitsPerEm; /** @@ -697,6 +698,20 @@ public final class OpenTypeFont antialias, fractionalMetrics); } + /** + * Fetches the raw glyph outline for the specified glyph index. This is used + * for the autofitter only ATM and is otherwise not usable for outside code. + * + * @param glyph the glyph index to fetch + * @param transform the transform to apply + * + * @return the raw outline of that glyph + */ + public synchronized Zone getRawGlyphOutline(int glyph, + AffineTransform transform) + { + return scaler.getRawOutline(glyph, transform); + } /** * Returns a name for the specified glyph. This is useful for diff --git a/libjava/classpath/gnu/java/awt/font/opentype/Scaler.java b/libjava/classpath/gnu/java/awt/font/opentype/Scaler.java index 499c3ea526c..83a31c576c7 100644 --- a/libjava/classpath/gnu/java/awt/font/opentype/Scaler.java +++ b/libjava/classpath/gnu/java/awt/font/opentype/Scaler.java @@ -37,6 +37,8 @@ exception statement from your version. */ package gnu.java.awt.font.opentype; +import gnu.java.awt.font.opentype.truetype.Zone; + import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; @@ -189,4 +191,14 @@ public abstract class Scaler boolean antialiased, boolean fractionalMetrics, boolean horizontal); + + /** + * Returns the raw outline data. This is used for the autofitter atm. + * + * @param glyph the glyph index + * @param transform the transform to apply + * + * @return the raw glyph outline + */ + public abstract Zone getRawOutline(int glyph, AffineTransform transform); } diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLoader.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLoader.java index b12d7782b23..3733afe92d8 100644 --- a/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLoader.java +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLoader.java @@ -119,6 +119,11 @@ final class GlyphLoader 0, 0); } + public void loadGlyph(int glyphIndex, AffineTransform transform, + Zone glyphZone) + { + loadGlyph(glyphIndex, unitsPerEm, transform, false, glyphZone); + } private void loadSubGlyph(int glyphIndex, double pointSize, diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/TrueTypeScaler.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/TrueTypeScaler.java index e4d7309cb55..8dfdeff0790 100644 --- a/libjava/classpath/gnu/java/awt/font/opentype/truetype/TrueTypeScaler.java +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/TrueTypeScaler.java @@ -198,6 +198,12 @@ public final class TrueTypeScaler return glyphZone.getPath(); } + public Zone getRawOutline(int glyphIndex, AffineTransform transform) + { + Zone zone = new Zone(glyphZone.getCapacity()); + glyphLoader.loadGlyph(glyphIndex, transform, zone); + return zone; + } /** * Determines the advance width and height for a glyph. diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/Zone.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/Zone.java index c0a3947f684..ff5bb631619 100644 --- a/libjava/classpath/gnu/java/awt/font/opentype/truetype/Zone.java +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/Zone.java @@ -45,7 +45,7 @@ import java.awt.geom.PathIterator; /** * A collection of points with some additional information. */ -final class Zone +public final class Zone { private final int[] pos; private final int[] origPos; diff --git a/libjava/classpath/gnu/java/awt/java2d/AbstractGraphics2D.java b/libjava/classpath/gnu/java/awt/java2d/AbstractGraphics2D.java index 9d017240d60..da21253980f 100644 --- a/libjava/classpath/gnu/java/awt/java2d/AbstractGraphics2D.java +++ b/libjava/classpath/gnu/java/awt/java2d/AbstractGraphics2D.java @@ -100,6 +100,20 @@ import java.util.Map; * {@link #updateRaster(Raster, int, int, int, int)} method, which always gets * called after a chunk of data got painted into the raster. * </p> + * <p>Alternativly the backend can provide a method for filling Shapes by + * overriding the protected method fillShape(). This can be accomplished + * by a polygon filling function of the backend. Keep in mind though that + * Shapes can be quite complex (i.e. non-convex and containing holes, etc) + * which is not supported by all polygon fillers. Also it must be noted + * that fillShape() is expected to handle painting and compositing as well as + * clipping and transformation. If your backend can't support this natively, + * then you can fallback to the implementation in this class. You'll need + * to provide a writable Raster then, see above.</p> + * <p>Another alternative is to implement fillScanline() which only requires + * the backend to be able to draw horizontal lines in device space, + * which is usually very cheap. + * The implementation should still handle painting and compositing, + * but no more clipping and transformation is required by the backend.</p> * <p>The backend is free to provide implementations for the various raw* * methods for optimized AWT 1.1 style painting of some primitives. This should * accelerate painting of Swing greatly. When doing so, the backend must also @@ -126,6 +140,9 @@ import java.util.Map; * in plain Java because they involve lots of shuffling around with large * arrays. In fact, you really would want to let the graphics card to the * work, they are made for this.</li> + * <li>Provide an accelerated implementation for fillShape(). For instance, + * OpenGL can fill shapes very efficiently. There are some considerations + * to be made though, see above for details.</li> * </ol> * </p> * @@ -144,6 +161,12 @@ public abstract class AbstractGraphics2D private static final int AA_SAMPLING = 8; /** + * Caches certain shapes to avoid massive creation of such Shapes in + * the various draw* and fill* methods. + */ + private static final ThreadLocal shapeCache = new ThreadLocal(); + + /** * The transformation for this Graphics2D instance */ protected AffineTransform transform; @@ -184,11 +207,6 @@ public abstract class AbstractGraphics2D private RenderingHints renderingHints; /** - * The paint raster. - */ - private Raster paintRaster; - - /** * The raster of the destination surface. This is where the painting is * performed. */ @@ -219,7 +237,7 @@ public abstract class AbstractGraphics2D * 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; + private boolean isOptimized = true; /** * Creates a new AbstractGraphics2D instance. @@ -270,7 +288,6 @@ public abstract class AbstractGraphics2D public boolean drawImage(Image image, AffineTransform xform, ImageObserver obs) { - boolean ret = false; Rectangle areaOfInterest = new Rectangle(0, 0, image.getWidth(obs), image.getHeight(obs)); return drawImageImpl(image, xform, obs, areaOfInterest); @@ -982,7 +999,8 @@ public abstract class AbstractGraphics2D else copy.clip = new GeneralPath(clip); - copy.renderingHints = new RenderingHints(renderingHints); + copy.renderingHints = new RenderingHints(null); + copy.renderingHints.putAll(renderingHints); copy.transform = new AffineTransform(transform); // The remaining state is inmmutable and doesn't need to be copied. return copy; @@ -1143,14 +1161,31 @@ public abstract class AbstractGraphics2D { if (isOptimized) { - int tx = (int) transform.getTranslateX(); - int ty = (int) transform.getTranslateY(); - rawDrawLine(x1 + tx, y1 + ty, x2 + tx, y2 + ty); + rawDrawLine(x1, y1, x2, y2); } else { - Line2D line = new Line2D.Double(x1, y1, x2, y2); - draw(line); + ShapeCache sc = getShapeCache(); + if (sc.line == null) + sc.line = new Line2D.Float(); + sc.line.setLine(x1, y1, x2, y2); + draw(sc.line); + } + } + + public void drawRect(int x, int y, int w, int h) + { + if (isOptimized) + { + rawDrawRect(x, y, w, h); + } + else + { + ShapeCache sc = getShapeCache(); + if (sc.rect == null) + sc.rect = new Rectangle(); + sc.rect.setBounds(x, y, w, h); + draw(sc.rect); } } @@ -1166,13 +1201,15 @@ public abstract class AbstractGraphics2D { if (isOptimized) { - int tx = (int) transform.getTranslateX(); - int ty = (int) transform.getTranslateY(); - rawFillRect(x + tx, y + ty, width, height); + rawFillRect(x, y, width, height); } else { - fill(new Rectangle(x, y, width, height)); + ShapeCache sc = getShapeCache(); + if (sc.rect == null) + sc.rect = new Rectangle(); + sc.rect.setBounds(x, y, width, height); + fill(sc.rect); } } @@ -1213,8 +1250,11 @@ public abstract class AbstractGraphics2D 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)); + ShapeCache sc = getShapeCache(); + if (sc.roundRect == null) + sc.roundRect = new RoundRectangle2D.Float(); + sc.roundRect.setRoundRect(x, y, width, height, arcWidth, arcHeight); + draw(sc.roundRect); } /** @@ -1230,8 +1270,11 @@ public abstract class AbstractGraphics2D 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)); + ShapeCache sc = getShapeCache(); + if (sc.roundRect == null) + sc.roundRect = new RoundRectangle2D.Float(); + sc.roundRect.setRoundRect(x, y, width, height, arcWidth, arcHeight); + fill(sc.roundRect); } /** @@ -1244,7 +1287,11 @@ public abstract class AbstractGraphics2D */ public void drawOval(int x, int y, int width, int height) { - draw(new Ellipse2D.Double(x, y, width, height)); + ShapeCache sc = getShapeCache(); + if (sc.ellipse == null) + sc.ellipse = new Ellipse2D.Float(); + sc.ellipse.setFrame(x, y, width, height); + draw(sc.ellipse); } /** @@ -1257,7 +1304,11 @@ public abstract class AbstractGraphics2D */ public void fillOval(int x, int y, int width, int height) { - fill(new Ellipse2D.Double(x, y, width, height)); + ShapeCache sc = getShapeCache(); + if (sc.ellipse == null) + sc.ellipse = new Ellipse2D.Float(); + sc.ellipse.setFrame(x, y, width, height); + fill(sc.ellipse); } /** @@ -1266,8 +1317,11 @@ public abstract class AbstractGraphics2D 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)); + ShapeCache sc = getShapeCache(); + if (sc.arc == null) + sc.arc = new Arc2D.Float(); + sc.arc.setArc(x, y, width, height, arcStart, arcAngle, Arc2D.OPEN); + draw(sc.arc); } /** @@ -1276,8 +1330,11 @@ public abstract class AbstractGraphics2D 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)); + ShapeCache sc = getShapeCache(); + if (sc.arc == null) + sc.arc = new Arc2D.Float(); + sc.arc.setArc(x, y, width, height, arcStart, arcAngle, Arc2D.PIE); + draw(sc.arc); } public void drawPolyline(int[] xPoints, int[] yPoints, int npoints) @@ -1291,7 +1348,13 @@ public abstract class AbstractGraphics2D */ public void drawPolygon(int[] xPoints, int[] yPoints, int npoints) { - draw(new Polygon(xPoints, yPoints, npoints)); + ShapeCache sc = getShapeCache(); + if (sc.polygon == null) + sc.polygon = new Polygon(); + sc.polygon.xpoints = xPoints; + sc.polygon.ypoints = yPoints; + sc.polygon.npoints = npoints; + draw(sc.polygon); } /** @@ -1299,7 +1362,13 @@ public abstract class AbstractGraphics2D */ public void fillPolygon(int[] xPoints, int[] yPoints, int npoints) { - fill(new Polygon(xPoints, yPoints, npoints)); + ShapeCache sc = getShapeCache(); + if (sc.polygon == null) + sc.polygon = new Polygon(); + sc.polygon.xpoints = xPoints; + sc.polygon.ypoints = yPoints; + sc.polygon.npoints = npoints; + fill(sc.polygon); } /** @@ -1460,8 +1529,12 @@ public abstract class AbstractGraphics2D } /** - * Fills the specified shape. The shape has already been clipped against the - * current clip. + * Fills the specified shape. Override this if your backend can efficiently + * fill shapes. This is possible on many systems via a polygon fill + * method or something similar. But keep in mind that Shapes can be quite + * complex (non-convex, with holes etc), which is not necessarily supported + * by all polygon fillers. Also note that you must perform clipping + * before filling the shape. * * @param s the shape to fill * @param isFont <code>true</code> if the shape is a font outline @@ -1533,6 +1606,11 @@ public abstract class AbstractGraphics2D draw(new Line2D.Float(x0, y0, x1, y1)); } + protected void rawDrawRect(int x, int y, int w, int h) + { + draw(new Rectangle(x, y, w, h)); + } + /** * Draws a string in optimization mode. The implementation should respect the * clip and translation. It can assume that the clip is a rectangle and that @@ -1627,11 +1705,7 @@ public abstract class AbstractGraphics2D } /** - * 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. + * Fills the specified polygon without anti-aliasing. */ private void fillShapeImpl(ArrayList segs, Rectangle2D deviceBounds2D, Rectangle2D userBounds, @@ -1662,7 +1736,7 @@ public abstract class AbstractGraphics2D for (Iterator i = segs.iterator(); i.hasNext();) { PolyEdge edge = (PolyEdge) i.next(); - int yindex = (int) ((int) Math.ceil(edge.y0) - (int) Math.ceil(icMinY)); + int yindex = (int) Math.ceil(edge.y0) - (int) Math.ceil(icMinY); if (edgeTable[yindex] == null) // Create bucket when needed. edgeTable[yindex] = new ArrayList(); edgeTable[yindex].add(edge); // Add edge to the bucket of its line. @@ -1766,7 +1840,8 @@ public abstract class AbstractGraphics2D } /** - * Paints a scanline between x0 and x1. + * Paints a scanline between x0 and x1. Override this when your backend + * can efficiently draw/fill horizontal lines. * * @param x0 the left offset * @param x1 the right offset @@ -1972,8 +2047,7 @@ public abstract class AbstractGraphics2D // Render full scanline. //System.err.println("scanline: " + y); if (! emptyScanline) - fillScanlineAA(alpha, leftX, (int) y, rightX - leftX, pCtx, - (int) minX); + fillScanlineAA(alpha, leftX, y, rightX - leftX, pCtx, (int) minX); } pCtx.dispose(); @@ -1986,7 +2060,7 @@ public abstract class AbstractGraphics2D * * @param alpha the alpha values in the scanline * @param x0 the beginning of the scanline - * @param y the y coordinate of the line + * @param yy the y coordinate of the line */ private void fillScanlineAA(int[] alpha, int x0, int yy, int numPixels, PaintContext pCtx, int offs) @@ -1997,7 +2071,6 @@ public abstract class AbstractGraphics2D Raster paintRaster = pCtx.getRaster(x0, yy, numPixels, 1); //System.err.println("paintColorModel: " + pCtx.getColorModel()); WritableRaster aaRaster = paintRaster.createCompatibleWritableRaster(); - int numBands = paintRaster.getNumBands(); ColorModel cm = pCtx.getColorModel(); double lastAlpha = 0.; int lastAlphaInt = 0; @@ -2156,10 +2229,10 @@ public abstract class AbstractGraphics2D 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 x2 = rect.x; + int y2 = rect.y; + int w2 = rect.width; + int h2 = rect.height; int dx = (x > x2) ? x : x2; int dy = (y > y2) ? y : y2; @@ -2266,4 +2339,20 @@ public abstract class AbstractGraphics2D deviceBounds.setRect(minX, minY, maxX - minX, maxY - minY); return segs; } + + /** + * Returns the ShapeCache for the calling thread. + * + * @return the ShapeCache for the calling thread + */ + private ShapeCache getShapeCache() + { + ShapeCache sc = (ShapeCache) shapeCache.get(); + if (sc == null) + { + sc = new ShapeCache(); + shapeCache.set(sc); + } + return sc; + } } diff --git a/libjava/classpath/gnu/java/awt/java2d/QuadSegment.java b/libjava/classpath/gnu/java/awt/java2d/QuadSegment.java index 5e15fe881d8..97a5372f6bd 100644 --- a/libjava/classpath/gnu/java/awt/java2d/QuadSegment.java +++ b/libjava/classpath/gnu/java/awt/java2d/QuadSegment.java @@ -145,7 +145,52 @@ public class QuadSegment extends Segment Point2D cp; QuadSegment s; - if( plus ) + if(!plus) + { + n1[0] = -n1[0]; + n1[1] = -n1[1]; + n2[0] = -n2[0]; + n2[1] = -n2[1]; + } + + // Handle special cases where the control point is equal to an end point + // or end points are equal (ie, straight lines) + if (curve.getP1().equals(curve.getCtrlPt())) + { + cp = curve.getCtrlPt(); + cp.setLocation(cp.getX() + n2[0], cp.getY() + n2[1]); + n1[0] = n2[0]; + n1[1] = n2[1]; + } + else if (curve.getP2().equals(curve.getCtrlPt())) + { + cp = curve.getCtrlPt(); + cp.setLocation(cp.getX() + n1[0], cp.getY() + n1[1]); + n2[0] = n1[0]; + n2[1] = n1[1]; + } + else if (curve.getP1().equals(curve.getP2())) + { + cp = curve.getCtrlPt(); + + double deltaX = curve.getX1() - curve.getCtrlX(); + double deltaY = curve.getY1() - curve.getCtrlY(); + double length = Math.sqrt((deltaX * deltaX) + (deltaY * deltaY)); + double ratio = radius / length; + deltaX *= ratio; + deltaY *= ratio; + + if (plus) + cp.setLocation(cp.getX() + deltaX, cp.getY() + deltaY); + else + cp.setLocation(cp.getX() - deltaX, cp.getY() - deltaY); + } + else if (n1[0] == n2[0] && n1[1] == n2[1]) + { + cp = curve.getCtrlPt(); + cp.setLocation(cp.getX() + n1[0], cp.getY() + n1[1]); + } + else { cp = lineIntersection(curve.getX1() + n1[0], curve.getY1() + n1[1], @@ -155,25 +200,11 @@ public class QuadSegment extends Segment 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]); } + + 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; } diff --git a/libjava/classpath/gnu/java/awt/java2d/ShapeCache.java b/libjava/classpath/gnu/java/awt/java2d/ShapeCache.java new file mode 100644 index 00000000000..034b53cadd3 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/ShapeCache.java @@ -0,0 +1,85 @@ +/* ShapeCache.java -- Caches certain Shapes for reuse in AbstractGraphics2D + 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.Polygon; +import java.awt.Rectangle; +import java.awt.geom.Arc2D; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.RoundRectangle2D; + +/** + * Caches certain Shape objects for reuse in AbstractGraphics2D. This avoids + * massive creation of such objects. + */ +public class ShapeCache +{ + + /** + * A cached Line2D. + */ + public Line2D line; + + /** + * A cached Rectangle. + */ + public Rectangle rect; + + /** + * A cached RoundRectangle2D. + */ + public RoundRectangle2D roundRect; + + /** + * A cached Ellipse2D. + */ + public Ellipse2D ellipse; + + /** + * A cached Arc2D. + */ + public Arc2D arc; + + /** + * A cached Polygon. + */ + public Polygon polygon; + +} diff --git a/libjava/classpath/gnu/java/awt/java2d/TexturePaintContext.java b/libjava/classpath/gnu/java/awt/java2d/TexturePaintContext.java index 1a782ce07e3..db0a2e65804 100644 --- a/libjava/classpath/gnu/java/awt/java2d/TexturePaintContext.java +++ b/libjava/classpath/gnu/java/awt/java2d/TexturePaintContext.java @@ -104,7 +104,7 @@ public class TexturePaintContext double scaleY = anchor.getHeight() / image.getHeight(); transform = (AffineTransform) xform.clone(); transform.scale(scaleX, scaleY); - transform.translate(-anchor.getMinX(), -anchor.getMaxX()); + transform.translate(-anchor.getMinX(), -anchor.getMinY()); transform = transform.createInverse(); } catch (NoninvertibleTransformException ex) @@ -177,6 +177,12 @@ public class TexturePaintContext // The modulo operation gives us the replication effect. dx = ((dx - minX) % width) + minX; dy = ((dy - minY) % height) + minY; + + // Handle possible negative values (replicating above the top-left) + if (dx < 0) + dx += width; + if (dy < 0) + dy += height; // Copy the pixel. pixel = source.getDataElements(dx, dy, pixel); diff --git a/libjava/classpath/gnu/java/awt/peer/ClasspathFontPeer.java b/libjava/classpath/gnu/java/awt/peer/ClasspathFontPeer.java index dad7bb0b08f..2176f34a5f1 100644 --- a/libjava/classpath/gnu/java/awt/peer/ClasspathFontPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/ClasspathFontPeer.java @@ -832,18 +832,4 @@ public abstract class ClasspathFontPeer public abstract Rectangle2D getMaxCharBounds (Font font, FontRenderContext rc); - /** - * Implementation of {@link Font#getStringBounds(CharacterIterator, int, - * int, FontRenderContext)} - * - * @param font the font this peer is being called from. This may be - * useful if you are sharing peers between Font objects. Otherwise it may - * be ignored. - */ - - public abstract Rectangle2D getStringBounds (Font font, - CharacterIterator ci, - int begin, int limit, - FontRenderContext frc); - } diff --git a/libjava/classpath/gnu/java/awt/peer/GLightweightPeer.java b/libjava/classpath/gnu/java/awt/peer/GLightweightPeer.java index 88733b92f8c..f9a7bac8ee6 100644 --- a/libjava/classpath/gnu/java/awt/peer/GLightweightPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/GLightweightPeer.java @@ -54,25 +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; -/* - * Another possible implementation strategy for lightweight peers is - * to make GLightweightPeer a placeholder class that implements - * LightweightPeer. Then the Component and Container classes could - * identify a peer as lightweight and handle it specially. The - * current approach is probably more clear but less efficient. - */ - /** * A stub class that implements the ComponentPeer and ContainerPeer * interfaces using callbacks into the Component and Container @@ -85,47 +74,48 @@ import java.awt.peer.LightweightPeer; public class GLightweightPeer implements LightweightPeer, ContainerPeer { - private Component comp; - - private Insets containerInsets; - - public GLightweightPeer(Component comp) + public GLightweightPeer() { - this.comp = comp; + // Nothing to do here. } // -------- java.awt.peer.ContainerPeer implementation: public Insets insets() { - return getInsets (); + // Nothing to do here for lightweights. + return null; } public Insets getInsets() { - if (containerInsets == null) - containerInsets = new Insets (0,0,0,0); - return containerInsets; + // Nothing to do here for lightweights. + return null; } public void beginValidate() { + // Nothing to do here for lightweights. } public void endValidate() { + // Nothing to do here for lightweights. } public void beginLayout() { + // Nothing to do here for lightweights. } public void endLayout() { + // Nothing to do here for lightweights. } public boolean isPaintPending() { + // Nothing to do here for lightweights. return false; } @@ -133,122 +123,188 @@ public class GLightweightPeer public int checkImage(Image img, int width, int height, ImageObserver o) { - return comp.getToolkit().checkImage(img, width, height, o); + // Nothing to do here for lightweights. + return -1; } public Image createImage(ImageProducer prod) { - return comp.getToolkit().createImage(prod); + // Nothing to do here for lightweights. + return null; } /* This method is not called. */ public Image createImage(int width, int height) { + // Nothing to do here for lightweights. return null; } - public void disable() {} + public void disable() + { + // Nothing to do here for lightweights. + } - public void dispose() {} + public void dispose() + { + // Nothing to do here for lightweights. + } - public void enable() {} + public void enable() + { + // Nothing to do here for lightweights. + } public GraphicsConfiguration getGraphicsConfiguration() { + // Nothing to do here for lightweights. return null; } public FontMetrics getFontMetrics(Font f) { - return comp.getToolkit().getFontMetrics(f); + // We shouldn't end up here, but if we do we can still try do something + // reasonable. + Toolkit tk = Toolkit.getDefaultToolkit(); + return tk.getFontMetrics(f); } /* Returning null here tells the Component object that called us to * use its parent's Graphics. */ public Graphics getGraphics() { + // Nothing to do here for lightweights. return null; } public Point getLocationOnScreen() { - Point parentLocation = comp.getParent().getLocationOnScreen(); - return new Point (parentLocation.x + comp.getX(), - parentLocation.y + comp.getY()); + // Nothing to do here for lightweights. + return null; } public Dimension getMinimumSize() { - return new Dimension(comp.getWidth(), comp.getHeight()); + return minimumSize(); } - /* A lightweight component's preferred size is equivalent to its - * Component width and height values. */ public Dimension getPreferredSize() { - return new Dimension(comp.getWidth(), comp.getHeight()); + return preferredSize(); } /* Returning null here tells the Component object that called us to * use its parent's Toolkit. */ public Toolkit getToolkit() { + // Nothing to do here for lightweights. return null; } - public void handleEvent(AWTEvent e) {} + public void handleEvent(AWTEvent e) + { + // This can only happen when an application posts a PaintEvent for + // a lightweight component directly. We still support painting for + // this case. + if (e instanceof PaintEvent) + { + PaintEvent pe = (PaintEvent) e; + Component target = (Component) e.getSource(); + if (target != null && target.isShowing()) + { + Graphics g = target.getGraphics(); + if (g != null) + { + try + { + Rectangle clip = pe.getUpdateRect(); + g.setClip(clip); + target.paint(g); + } + finally + { + g.dispose(); + } + } + } + } + } - public void hide() {} + public void hide() + { + // Nothing to do here for lightweights. + } public boolean isFocusable() { + // Nothing to do here for lightweights. return false; } public boolean isFocusTraversable() { + // Nothing to do here for lightweights. return false; } public Dimension minimumSize() { - return getMinimumSize(); + return new Dimension(0, 0); } public Dimension preferredSize() { - return getPreferredSize(); + return new Dimension(0, 0); } - public void paint(Graphics graphics) {} + public void paint(Graphics graphics) + { + // Nothing to do here for lightweights. + } public boolean prepareImage(Image img, int width, int height, ImageObserver o) { - return comp.getToolkit().prepareImage(img, width, height, o); + // Nothing to do here for lightweights. + return false; } - public void print(Graphics graphics) {} + public void print(Graphics graphics) + { + // Nothing to do here for lightweights. + } public void repaint(long tm, int x, int y, int width, int height) { - Component p = comp.getParent(); - if (p != null) - p.repaint(tm, x + comp.getX(), y + comp.getY(), width, height); + // Nothing to do here for lightweights. } - public void requestFocus() {} + public void requestFocus() + { + // Nothing to do here for lightweights. + } - public boolean requestFocus(Component source, boolean bool1, boolean bool2, long x) + public boolean requestFocus(Component source, boolean bool1, boolean bool2, + long x) { + // Nothing to do here for lightweights. return false; } - public void reshape(int x, int y, int width, int height) {} + public void reshape(int x, int y, int width, int height) + { + // Nothing to do here for lightweights. + } - public void setBackground(Color color) {} + public void setBackground(Color color) + { + // Nothing to do here for lightweights. + } - public void setBounds(int x, int y, int width, int height) {} + public void setBounds(int x, int y, int width, int height) + { + // Nothing to do here for lightweights. + } /** * Sets the cursor on the heavy-weight parent peer. @@ -256,110 +312,141 @@ public class GLightweightPeer */ 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); - } + // Nothing to do here for lightweights. } - public void setEnabled(boolean enabled) {} + public void setEnabled(boolean enabled) + { + // Nothing to do here for lightweights. + } - public void setEventMask(long eventMask) {} + public void setEventMask(long eventMask) + { + // Nothing to do here for lightweights. + } - public void setFont(Font font) {} + public void setFont(Font font) + { + // Nothing to do here for lightweights. + } - public void setForeground(Color color) {} + public void setForeground(Color color) + { + // Nothing to do here for lightweights. + } - public void setVisible(boolean visible) {} + public void setVisible(boolean visible) + { + // Nothing to do here for lightweights. + } - public void show() {} + public void show() + { + // Nothing to do here for lightweights. + } - public ColorModel getColorModel () + public ColorModel getColorModel() { - return comp.getColorModel (); + // Nothing to do here for lightweights. + return null; } public boolean isObscured() { + // Nothing to do here for lightweights. return false; } public boolean canDetermineObscurity() { + // Nothing to do here for lightweights. return false; } - public void coalescePaintEvent(PaintEvent e) { } + public void coalescePaintEvent(PaintEvent e) + { + // Nothing to do here for lightweights. + } - public void updateCursorImmediately() { } + public void updateCursorImmediately() + { + // Nothing to do here for lightweights. + } public VolatileImage createVolatileImage(int width, int height) { + // Nothing to do here for lightweights. return null; } public boolean handlesWheelScrolling() { + // Nothing to do here for lightweights. return false; } public void createBuffers(int x, BufferCapabilities capabilities) - throws AWTException { } + throws AWTException + { + // Nothing to do here for lightweights. + } public Image getBackBuffer() { + // Nothing to do here for lightweights. return null; } - public void flip(BufferCapabilities.FlipContents contents) { } + public void flip(BufferCapabilities.FlipContents contents) + { + // Nothing to do here for lightweights. + } - public void destroyBuffers() { } + public void destroyBuffers() + { + // Nothing to do here for lightweights. + } public boolean isRestackSupported() { + // Nothing to do here for lightweights. return false; } public void cancelPendingPaint(int x, int y, int width, int height) { - + // Nothing to do here for lightweights. } public void restack() { - + // Nothing to do here for lightweights. } public Rectangle getBounds() { + // Nothing to do here for lightweights. return null; } public void reparent(ContainerPeer parent) { - + // Nothing to do here for lightweights. } public void setBounds(int x, int y, int z, int width, int height) { - + // Nothing to do here for lightweights. } public boolean isReparentSupported() { - return false; + // Nothing to do here for lightweights. + return true; } public void layout() { - + // Nothing to do here for lightweights. } } diff --git a/libjava/classpath/gnu/java/awt/peer/NativeEventLoopRunningEvent.java b/libjava/classpath/gnu/java/awt/peer/NativeEventLoopRunningEvent.java new file mode 100644 index 00000000000..962ecd9904e --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/NativeEventLoopRunningEvent.java @@ -0,0 +1,58 @@ +/* NativeEventLoopRunningEvent.java -- communicates to EventQueue the + state of the native event loop + 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; + +import java.awt.AWTEvent; + +public class NativeEventLoopRunningEvent + extends AWTEvent +{ + private boolean running; + + public NativeEventLoopRunningEvent(Object source) + { + super(source, 2999); + running = ((Boolean) source).booleanValue(); + } + + public boolean isRunning() + { + return running; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/AsyncImage.java b/libjava/classpath/gnu/java/awt/peer/gtk/AsyncImage.java new file mode 100644 index 00000000000..5238bfe7410 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/AsyncImage.java @@ -0,0 +1,283 @@ +/* AsyncImage.java -- Loads images asynchronously + 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.Graphics; +import java.awt.Image; +import java.awt.image.ImageConsumer; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; + +/** + * Supports asynchronous loading of images. + */ +public class AsyncImage + extends Image +{ + + /** + * Returned as source as long as the image is not complete. + */ + private class NullImageSource + implements ImageProducer + { + private ArrayList consumers; + + NullImageSource() + { + consumers = new ArrayList(); + } + + public void addConsumer(ImageConsumer ic) + { + consumers.add(ic); + } + + public boolean isConsumer(ImageConsumer ic) + { + return consumers.contains(ic); + } + + public void removeConsumer(ImageConsumer ic) + { + consumers.remove(ic); + } + + public void requestTopDownLeftRightResend(ImageConsumer ic) + { + startProduction(ic); + } + + public void startProduction(ImageConsumer ic) + { + consumers.add(ic); + for (int i = consumers.size() - 1; i >= 0; i--) + { + ImageConsumer c = (ImageConsumer) consumers.get(i); + c.setDimensions(1, 1); + ic.imageComplete(ImageConsumer.SINGLEFRAMEDONE); + } + } + + } + + /** + * Loads the image asynchronously. + */ + private class Loader + implements Runnable + { + private URL url; + Loader(URL u) + { + url = u; + } + + public void run() + { + Image image; + try + { + GtkImage gtkImage = new GtkImage(url); + image = CairoSurface.getBufferedImage(gtkImage); + } + catch (IllegalArgumentException iae) + { + image = null; + } + realImage = GtkToolkit.imageOrError(image); + synchronized (AsyncImage.this) + { + notifyObservers(ImageObserver.ALLBITS | ImageObserver.HEIGHT + | ImageObserver.WIDTH | ImageObserver.PROPERTIES); + observers = null; // Not needed anymore. + } + } + } + + /** + * The real image. This is null as long as the image is not complete. + */ + Image realImage; + + /** + * The image observers. + * + * This is package private to avoid accessor methods. + */ + HashSet observers; + + /** + * Creates a new AsyncImage that loads from the specified URL. + */ + AsyncImage(URL url) + { + observers = new HashSet(); + Loader l = new Loader(url); + Thread t = new Thread(l); + t.start(); + } + + public void flush() + { + // Nothing to do here. + } + + public Graphics getGraphics() + { + Image r = realImage; + Graphics g = null; + if (r != null) + g = r.getGraphics(); // Should we return some dummy graphics instead? + return g; + } + + public int getHeight(ImageObserver observer) + { + addObserver(observer); + int height = 0; + Image r = realImage; + if (r != null) + height = r.getHeight(observer); + return height; + } + + public Object getProperty(String name, ImageObserver observer) + { + addObserver(observer); + Image r = realImage; + Object prop = null; + if (r != null) + prop = r.getProperty(name, observer); + return prop; + } + + public ImageProducer getSource() + { + Image r = realImage; + ImageProducer source; + if (r == null) + source = new NullImageSource(); + else + source = r.getSource(); + return source; + } + + public int getWidth(ImageObserver observer) + { + addObserver(observer); + int width = 0; + Image r = realImage; + if (r != null) + width = r.getWidth(observer); + return width; + } + + void addObserver(ImageObserver obs) + { + if (obs != null) + { + synchronized (this) + { + // This field gets null when image loading is complete and we don't + // need to store any more observers. + HashSet observs = observers; + if (observs != null) + { + observs.add(obs); + } + else + { + // When the image is complete, notify the observer. Dunno if + // that's really needed, but to be sure. + obs.imageUpdate(this, ImageObserver.WIDTH + | ImageObserver.HEIGHT + |ImageObserver.ALLBITS + | ImageObserver.PROPERTIES, 0, 0, + realImage.getWidth(null), + realImage.getHeight(null)); + } + } + } + } + + static Image realImage(Image img, ImageObserver obs) + { + if (img instanceof AsyncImage) + { + ((AsyncImage) img).addObserver(obs); + Image r = ((AsyncImage) img).realImage; + if (r != null) + img = r; + } + return img; + } + + void notifyObservers(int status) + { + assert Thread.holdsLock(this); + // This field gets null when image loading is complete. + HashSet observs = observers; + if (observs != null) + { + Image r = realImage; + Iterator i = observs.iterator(); + while (i.hasNext()) + { + ImageObserver obs = (ImageObserver) i.next(); + obs.imageUpdate(this, status, 0, 0, r.getWidth(null), + r.getHeight(null)); + } + } + } + + int checkImage(ImageObserver obs) + { + addObserver(obs); + int flags = 0; + if (realImage != null) + flags = ImageObserver.ALLBITS | ImageObserver.WIDTH + | ImageObserver.HEIGHT | ImageObserver.PROPERTIES; + return flags; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/BufferedImageGraphics.java b/libjava/classpath/gnu/java/awt/peer/gtk/BufferedImageGraphics.java index 6a74eabc5d6..c792645d3e8 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/BufferedImageGraphics.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/BufferedImageGraphics.java @@ -38,22 +38,27 @@ exception statement from your version. */ package gnu.java.awt.peer.gtk; +import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Graphics; +import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.Image; import java.awt.Rectangle; import java.awt.Shape; +import java.awt.Toolkit; import java.awt.font.GlyphVector; import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; -import java.awt.image.DataBuffer; -import java.awt.image.DataBufferInt; import java.awt.image.ColorModel; -import java.awt.image.DirectColorModel; -import java.awt.image.RenderedImage; +import java.awt.image.DataBufferInt; import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.SinglePixelPackedSampleModel; import java.util.WeakHashMap; /** @@ -67,7 +72,13 @@ public class BufferedImageGraphics extends CairoGraphics2D /** * the buffered Image. */ - private BufferedImage image; + private BufferedImage image, buffer; + + /** + * Allows us to lock the image from updates (if we want to perform a few + * intermediary operations on the cairo surface, then update it all at once) + */ + private boolean locked; /** * Image size. @@ -89,12 +100,6 @@ public class BufferedImageGraphics extends CairoGraphics2D */ private long cairo_t; - /** - * Colormodels we recognize for fast copying. - */ - static ColorModel rgb32 = new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF); - static ColorModel argb32 = new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF, - 0xFF000000); private boolean hasFastCM; private boolean hasAlpha; @@ -104,15 +109,19 @@ public class BufferedImageGraphics extends CairoGraphics2D this.image = bi; imageWidth = bi.getWidth(); imageHeight = bi.getHeight(); - if(bi.getColorModel().equals(rgb32)) + locked = false; + + if (!(image.getSampleModel() instanceof SinglePixelPackedSampleModel)) + hasFastCM = false; + else if(bi.getColorModel().equals(CairoSurface.cairoCM_opaque)) { hasFastCM = true; hasAlpha = false; } - else if(bi.getColorModel().equals(argb32)) + else if(bi.getColorModel().equals(CairoSurface.cairoColorModel)) { hasFastCM = true; - hasAlpha = false; + hasAlpha = true; } else hasFastCM = false; @@ -128,27 +137,45 @@ public class BufferedImageGraphics extends CairoGraphics2D cairo_t = surface.newCairoContext(); - DataBuffer db = bi.getRaster().getDataBuffer(); + // Get pixels out of buffered image and set in cairo surface + Raster raster = bi.getRaster(); int[] pixels; - // get pixels - if(db instanceof CairoSurface) - pixels = ((CairoSurface)db).getPixels(imageWidth * imageHeight); + if (hasFastCM) + { + SinglePixelPackedSampleModel sm = (SinglePixelPackedSampleModel)image.getSampleModel(); + int minX = image.getRaster().getSampleModelTranslateX(); + int minY = image.getRaster().getSampleModelTranslateY(); + + // Pull pixels directly out of data buffer + if(raster instanceof CairoSurface) + pixels = ((CairoSurface)raster).getPixels(raster.getWidth() * raster.getHeight()); + else + pixels = ((DataBufferInt)raster.getDataBuffer()).getData(); + + // Discard pixels that fall outside of the image's bounds + // (ie, this image is actually a subimage of a different image) + if (!(sm.getScanlineStride() == imageWidth && minX == 0 && minY == 0)) + { + int[] pixels2 = new int[imageWidth * imageHeight]; + int scanline = sm.getScanlineStride(); + + for (int i = 0; i < imageHeight; i++) + System.arraycopy(pixels, (i - minY) * scanline - minX, pixels2, i * imageWidth, imageWidth); + + pixels = pixels2; + } + + // Fill the alpha channel as opaque if image does not have alpha + if( !hasAlpha ) + for(int i = 0; i < pixels.length; i++) + pixels[i] &= 0xFFFFFFFF; + } else { - if( hasFastCM ) - { - pixels = ((DataBufferInt)db).getData(); - if( !hasAlpha ) - for(int i = 0; i < pixels.length; i++) - pixels[i] &= 0xFFFFFFFF; - } - else - { - pixels = CairoGraphics2D.findSimpleIntegerArray - (image.getColorModel(),image.getData()); - } + pixels = CairoGraphics2D.findSimpleIntegerArray(image.getColorModel(),image.getData()); } + surface.setPixels( pixels ); setup( cairo_t ); @@ -157,12 +184,17 @@ public class BufferedImageGraphics extends CairoGraphics2D BufferedImageGraphics(BufferedImageGraphics copyFrom) { + image = copyFrom.image; surface = copyFrom.surface; cairo_t = surface.newCairoContext(); imageWidth = copyFrom.imageWidth; imageHeight = copyFrom.imageHeight; + locked = false; + + hasFastCM = copyFrom.hasFastCM; + hasAlpha = copyFrom.hasAlpha; + copy( copyFrom, cairo_t ); - setClip(0, 0, surface.width, surface.height); } /** @@ -170,25 +202,82 @@ public class BufferedImageGraphics extends CairoGraphics2D */ private void updateBufferedImage(int x, int y, int width, int height) { + if (locked) + return; + + double[] points = new double[]{x, y, width+x, height+y}; + transform.transform(points, 0, points, 0, 2); + x = (int)points[0]; + y = (int)points[1]; + width = (int)Math.ceil(points[2] - points[0]); + height = (int)Math.ceil(points[3] - points[1]); + int[] pixels = surface.getPixels(imageWidth * imageHeight); if( x > imageWidth || y > imageHeight ) return; + + // Deal with negative width/height. + if (height < 0) + { + y += height; + height = -height; + } + if (width < 0) + { + x += width; + width = -width; + } + // Clip edges. - if( x < 0 ){ width = width + x; x = 0; } - if( y < 0 ){ height = height + y; y = 0; } + if( x < 0 ) + x = 0; + if( y < 0 ) + y = 0; + if( x + width > imageWidth ) width = imageWidth - x; if( y + height > imageHeight ) height = imageHeight - y; - - if( !hasFastCM ) - image.setRGB(x, y, width, height, pixels, - x + y * imageWidth, imageWidth); + + if(!hasFastCM) + { + image.setRGB(x, y, width, height, pixels, + x + y * imageWidth, imageWidth); + // The setRGB method assumes (or should assume) that pixels are NOT + // alpha-premultiplied, but Cairo stores data with premultiplication + // (thus the pixels returned in getPixels are premultiplied). + // This is ignored for consistency, however, since in + // CairoGrahpics2D.drawImage we also use non-premultiplied data + + } else - System.arraycopy(pixels, y * imageWidth, - ((DataBufferInt)image.getRaster().getDataBuffer()). - getData(), y * imageWidth, height * imageWidth); + { + int[] db = ((DataBufferInt)image.getRaster().getDataBuffer()). + getData(); + + // This should not fail, as we check the image sample model when we + // set the hasFastCM flag + SinglePixelPackedSampleModel sm = (SinglePixelPackedSampleModel)image.getSampleModel() ; + + int minX = image.getRaster().getSampleModelTranslateX(); + int minY = image.getRaster().getSampleModelTranslateY(); + + if (sm.getScanlineStride() == imageWidth && minX == 0) + { + System.arraycopy(pixels, y * imageWidth, + db, (y - minY) * imageWidth, + height * imageWidth); + } + else + { + int scanline = sm.getScanlineStride(); + for (int i = y; i < (height + y); i++) + System.arraycopy(pixels, i * imageWidth + x, db, + (i - minY) * scanline + x - minX, width); + + } + } } /** @@ -221,36 +310,246 @@ public class BufferedImageGraphics extends CairoGraphics2D */ public void draw(Shape s) { - super.draw(s); - Rectangle r = s.getBounds(); - updateBufferedImage(r.x, r.y, r.width, r.height); + // Find total bounds of shape + Rectangle r = findStrokedBounds(s); + if (shiftDrawCalls) + { + r.width++; + r.height++; + } + + // Do the drawing + if (comp == null || comp instanceof AlphaComposite) + { + super.draw(s); + updateBufferedImage(r.x, r.y, r.width, r.height); + } + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setStroke(this.getStroke()); + g2d.setColor(this.getColor()); + g2d.setTransform(transform); + g2d.draw(s); + + drawComposite(r.getBounds2D(), null); + } } public void fill(Shape s) { - super.fill(s); - Rectangle r = s.getBounds(); - updateBufferedImage(r.x, r.y, r.width, r.height); + if (comp == null || comp instanceof AlphaComposite) + { + super.fill(s); + Rectangle r = s.getBounds(); + updateBufferedImage(r.x, r.y, r.width, r.height); + } + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setPaint(this.getPaint()); + g2d.setColor(this.getColor()); + g2d.setTransform(transform); + g2d.fill(s); + + drawComposite(s.getBounds2D(), null); + } } public void drawRenderedImage(RenderedImage image, AffineTransform xform) { - super.drawRenderedImage(image, xform); - updateBufferedImage(0, 0, imageWidth, imageHeight); + if (comp == null || comp instanceof AlphaComposite) + { + super.drawRenderedImage(image, xform); + updateBufferedImage(0, 0, imageWidth, imageHeight); + } + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setRenderingHints(this.getRenderingHints()); + g2d.setTransform(transform); + g2d.drawRenderedImage(image, xform); + + drawComposite(buffer.getRaster().getBounds(), null); + } + } protected boolean drawImage(Image img, AffineTransform xform, Color bgcolor, ImageObserver obs) { - boolean rv = super.drawImage(img, xform, bgcolor, obs); - updateBufferedImage(0, 0, imageWidth, imageHeight); - return rv; + if (comp == null || comp instanceof AlphaComposite) + { + boolean rv = super.drawImage(img, xform, bgcolor, obs); + updateBufferedImage(0, 0, imageWidth, imageHeight); + return rv; + } + else + { + // Get buffered image of source + if( !(img instanceof BufferedImage) ) + { + ImageProducer source = img.getSource(); + if (source == null) + return false; + img = Toolkit.getDefaultToolkit().createImage(source); + } + BufferedImage bImg = (BufferedImage) img; + + // Find translated bounds + Point2D origin = new Point2D.Double(bImg.getMinX(), bImg.getMinY()); + Point2D pt = new Point2D.Double(bImg.getWidth() + bImg.getMinX(), + bImg.getHeight() + bImg.getMinY()); + if (xform != null) + { + origin = xform.transform(origin, origin); + pt = xform.transform(pt, pt); + } + + // Create buffer and draw image + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setRenderingHints(this.getRenderingHints()); + g2d.drawImage(img, xform, obs); + + // Perform compositing + return drawComposite(new Rectangle2D.Double(origin.getX(), + origin.getY(), + pt.getX(), pt.getY()), + obs); + } } public void drawGlyphVector(GlyphVector gv, float x, float y) { - super.drawGlyphVector(gv, x, y); - updateBufferedImage(0, 0, imageWidth, imageHeight); + // Find absolute bounds, in user-space, of this glyph vector + Rectangle2D bounds = gv.getLogicalBounds(); + bounds = new Rectangle2D.Double(x + bounds.getX(), y + bounds.getY(), + bounds.getWidth(), bounds.getHeight()); + + // Perform draw operation + if (comp == null || comp instanceof AlphaComposite) + { + super.drawGlyphVector(gv, x, y); + updateBufferedImage((int)bounds.getX(), (int)bounds.getY(), + (int)bounds.getWidth(), (int)bounds.getHeight()); + } + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setPaint(this.getPaint()); + g2d.setStroke(this.getStroke()); + g2d.setTransform(transform); + g2d.drawGlyphVector(gv, x, y); + + drawComposite(bounds, null); + } + } + + /** + * Perform composite drawing from the buffer onto the main image. + * + * The image to be composited should already be drawn into the buffer, in the + * proper place, after all necessary transforms have been applied. + * + * @param bounds The bounds to draw, in user-space. + * @param observer The image observer, if any (may be null). + * @return True on success, false on failure. + */ + private boolean drawComposite(Rectangle2D bounds, ImageObserver observer) + { + // Find bounds in device space + double[] points = new double[] {bounds.getX(), bounds.getY(), + bounds.getMaxX(), bounds.getMaxY()}; + transform.transform(points, 0, points, 0, 2); + bounds = new Rectangle2D.Double(points[0], points[1], + (points[2] - points[0]), + (points[3] - points[1])); + + // Clip bounds by the stored clip, and by the internal buffer + Rectangle2D devClip = this.getClipInDevSpace(); + Rectangle2D.intersect(bounds, devClip, bounds); + devClip = new Rectangle(buffer.getMinX(), buffer.getMinY(), + buffer.getWidth(), buffer.getHeight()); + Rectangle2D.intersect(bounds, devClip, bounds); + + // Round bounds as needed, but be conservative in our rounding + // (otherwise it may leave unpainted stripes) + double x = bounds.getX(); + double y = bounds.getY(); + double w = bounds.getWidth(); + double h = bounds.getHeight(); + if (Math.floor(x) != x) + w--; + if (Math.floor(y) != y) + h--; + bounds.setRect(Math.ceil(x), Math.ceil(y), Math.floor(w), Math.floor(h)); + + // Find subimage of internal buffer for updating + BufferedImage buffer2 = buffer; + if (!bounds.equals(buffer2.getRaster().getBounds())) + buffer2 = buffer2.getSubimage((int)bounds.getX(), (int)bounds.getY(), + (int)bounds.getWidth(), + (int)bounds.getHeight()); + + // Find subimage of main image for updating + BufferedImage current = image; + current = current.getSubimage((int)bounds.getX(), (int)bounds.getY(), + (int)bounds.getWidth(), + (int)bounds.getHeight()); + + // Perform actual composite operation + compCtx.compose(buffer2.getRaster(), current.getRaster(), + current.getRaster()); + + // Prevent the clearRect in CairoGraphics2D.drawImage from clearing + // our composited image + locked = true; + + // This MUST call directly into the "action" method in CairoGraphics2D, + // not one of the wrappers, to ensure that the composite isn't processed + // more than once! + boolean rv = super.drawImage(current, + AffineTransform.getTranslateInstance(bounds.getX(), + bounds.getY()), + new Color(0,0,0,0), null); + locked = false; + return rv; + } + + private void createBuffer() + { + if (buffer == null) + { + buffer = new BufferedImage(image.getWidth(), image.getHeight(), + BufferedImage.TYPE_INT_ARGB); + } + else + { + Graphics2D g2d = ((Graphics2D)buffer.getGraphics()); + + g2d.setBackground(new Color(0,0,0,0)); + g2d.clearRect(0, 0, buffer.getWidth(), buffer.getHeight()); + } + } + + protected ColorModel getNativeCM() + { + return image.getColorModel(); + } + + protected ColorModel getBufferCM() + { + return ColorModel.getRGBdefault(); } } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/CairoGraphics2D.java b/libjava/classpath/gnu/java/awt/peer/gtk/CairoGraphics2D.java index b665f562e25..348801800d2 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/CairoGraphics2D.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/CairoGraphics2D.java @@ -45,6 +45,7 @@ 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.GradientPaint; @@ -53,6 +54,8 @@ import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.Image; import java.awt.Paint; +import java.awt.PaintContext; +import java.awt.Point; import java.awt.Polygon; import java.awt.Rectangle; import java.awt.RenderingHints; @@ -68,6 +71,7 @@ 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.Point2D; @@ -131,6 +135,7 @@ public abstract class CairoGraphics2D extends Graphics2D * The current paint */ Paint paint; + boolean customPaint; /** * The current stroke @@ -161,6 +166,7 @@ public abstract class CairoGraphics2D extends Graphics2D * The current compositing context, if any. */ Composite comp; + CompositeContext compCtx; /** * Rendering hint map. @@ -172,7 +178,7 @@ public abstract class CairoGraphics2D extends Graphics2D * coords be shifted to land on 0.5-pixel boundaries, in order to land on * "middle of pixel" coordinates and light up complete pixels. */ - private boolean shiftDrawCalls = false; + protected boolean shiftDrawCalls = false; /** * Keep track if the first clip to be set, which is restored on setClip(null); @@ -190,6 +196,18 @@ public abstract class CairoGraphics2D extends Graphics2D 0xFF000000); /** + * Native constants for interpolation methods. + * Note, this corresponds to an enum in native/jni/gtk-peer/cairographics2d.h + */ + public static final int INTERPOLATION_NEAREST = 0, + INTERPOLATION_BILINEAR = 1, + INTERPOLATION_BICUBIC = 5, + ALPHA_INTERPOLATION_SPEED = 2, + ALPHA_INTERPOLATION_QUALITY = 3, + ALPHA_INTERPOLATION_DEFAULT = 4; + // TODO: Does ALPHA_INTERPOLATION really correspond to CAIRO_FILTER_FAST/BEST/GOOD? + + /** * Constructor does nothing. */ public CairoGraphics2D() @@ -204,7 +222,7 @@ public abstract class CairoGraphics2D extends Graphics2D { nativePointer = init(cairo_t_pointer); setRenderingHints(new RenderingHints(getDefaultHints())); - font = new Font("SansSerif", Font.PLAIN, 12); + setFont(new Font("SansSerif", Font.PLAIN, 12)); setColor(Color.black); setBackground(Color.white); setPaint(Color.black); @@ -239,6 +257,8 @@ public abstract class CairoGraphics2D extends Graphics2D bg = new Color(g.bg.getRGB()); } + firstClip = g.firstClip; + originalClip = g.originalClip; clip = g.getClip(); if (g.transform == null) @@ -246,14 +266,14 @@ public abstract class CairoGraphics2D extends Graphics2D else transform = new AffineTransform(g.transform); - font = g.font; - + setFont(g.font); setColor(foreground); setBackground(bg); setPaint(paint); setStroke(stroke); setTransformImpl(transform); setClip(clip); + setComposite(comp); } /** @@ -274,6 +294,8 @@ public abstract class CairoGraphics2D extends Graphics2D { disposeNative(nativePointer); nativePointer = 0; + if (compCtx != null) + compCtx.dispose(); } /** @@ -293,6 +315,11 @@ public abstract class CairoGraphics2D extends Graphics2D int width, int height, int dx, int dy); + /** + * Find the bounds of this graphics context, in device space. + * + * @return the bounds in device-space + */ protected abstract Rectangle2D getRealBounds(); ////// Native Methods //////////////////////////////////////////////////// @@ -309,15 +336,17 @@ public abstract class CairoGraphics2D extends Graphics2D * @param i2u - affine transform array */ private native void drawPixels(long pointer, int[] pixels, int w, int h, - int stride, double[] i2u, double alpha); + int stride, double[] i2u, double alpha, + int interpolation); private native void setGradient(long pointer, double x1, double y1, double x2, double y2, int r1, int g1, int b1, int a1, int r2, int g2, int b2, int a2, boolean cyclic); - private native void setTexturePixels(long pointer, int[] pixels, int w, - int h, int stride); + private native void setPaintPixels(long pointer, int[] pixels, int w, + int h, int stride, boolean repeat, + int x, int y); /** * Set the current transform matrix @@ -365,6 +394,10 @@ public abstract class CairoGraphics2D extends Graphics2D float x, float y, int n, int[] codes, float[] positions); + /** + * Set the font in cairo. + */ + private native void cairoSetFont(long pointer, GdkFontPeer font); private native void cairoRelCurveTo(long pointer, double dx1, double dy1, double dx2, double dy2, double dx3, @@ -441,11 +474,6 @@ public abstract class CairoGraphics2D extends Graphics2D private native void cairoResetClip(long pointer); /** - * Set interpolation types - */ - private native void cairoSurfaceSetFilter(long pointer, int filter); - - /** * Draws a line from (x1,y1) to (x2,y2). * * @param pointer the native pointer @@ -666,13 +694,14 @@ public abstract class CairoGraphics2D extends Graphics2D public void setPaint(Paint p) { - if (paint == null) + if (p == null) return; paint = p; if (paint instanceof Color) { setColor((Color) paint); + customPaint = false; } else if (paint instanceof TexturePaint) { @@ -690,7 +719,8 @@ public abstract class CairoGraphics2D extends Graphics2D AffineTransformOp op = new AffineTransformOp(at, getRenderingHints()); BufferedImage texture = op.filter(img, null); int[] pixels = texture.getRGB(0, 0, width, height, null, 0, width); - setTexturePixels(nativePointer, pixels, width, height, width); + setPaintPixels(nativePointer, pixels, width, height, width, true, 0, 0); + customPaint = false; } else if (paint instanceof GradientPaint) { @@ -703,9 +733,108 @@ public abstract class CairoGraphics2D extends Graphics2D c1.getRed(), c1.getGreen(), c1.getBlue(), c1.getAlpha(), c2.getRed(), c2.getGreen(), c2.getBlue(), c2.getAlpha(), gp.isCyclic()); + customPaint = false; } else - throw new java.lang.UnsupportedOperationException(); + { + customPaint = true; + } + } + + /** + * Sets a custom paint + * + * @param bounds the bounding box, in user space + */ + protected void setCustomPaint(Rectangle bounds) + { + if (paint instanceof Color || paint instanceof TexturePaint + || paint instanceof GradientPaint) + return; + + int userX = bounds.x; + int userY = bounds.y; + int userWidth = bounds.width; + int userHeight = bounds.height; + + // Find bounds in device space + Point2D origin = transform.transform(new Point2D.Double(userX, userY), + null); + Point2D extreme = transform.transform(new Point2D.Double(userWidth + userX, + userHeight + userY), + null); + int deviceX = (int)origin.getX(); + int deviceY = (int)origin.getY(); + int deviceWidth = (int)Math.ceil(extreme.getX() - origin.getX()); + int deviceHeight = (int)Math.ceil(extreme.getY() - origin.getY()); + + // Get raster of the paint background + PaintContext pc = paint.createContext(CairoSurface.cairoColorModel, + new Rectangle(deviceX, deviceY, + deviceWidth, + deviceHeight), + bounds, + transform, hints); + + Raster raster = pc.getRaster(deviceX, deviceY, deviceWidth, + deviceHeight); + + // Clear the transform matrix in Cairo, since the raster returned by the + // PaintContext is already in device-space + AffineTransform oldTx = new AffineTransform(transform); + setTransformImpl(new AffineTransform()); + + // Set pixels in cairo, aligning the top-left of the background image + // to the top-left corner in device space + if (pc.getColorModel().equals(CairoSurface.cairoColorModel) + && raster.getSampleModel().getTransferType() == DataBuffer.TYPE_INT) + { + // Use a fast copy if the paint context can uses a Cairo-compatible + // color model + setPaintPixels(nativePointer, + (int[])raster.getDataElements(0, 0, deviceWidth, + deviceHeight, null), + deviceWidth, deviceHeight, deviceWidth, false, + deviceX, deviceY); + } + + else if (pc.getColorModel().equals(CairoSurface.cairoCM_opaque) + && raster.getSampleModel().getTransferType() == DataBuffer.TYPE_INT) + { + // We can also optimize if the context uses a similar color model + // but without an alpha channel; we just add the alpha + int[] pixels = (int[])raster.getDataElements(0, 0, deviceWidth, + deviceHeight, null); + + for (int i = 0; i < pixels.length; i++) + pixels[i] = 0xff000000 | (pixels[i] & 0x00ffffff); + + setPaintPixels(nativePointer, pixels, deviceWidth, deviceHeight, + deviceWidth, false, deviceX, deviceY); + } + + else + { + // Fall back on wrapping the raster in a BufferedImage, and + // use BufferedImage.getRGB() to do color-model conversion + WritableRaster wr = Raster.createWritableRaster(raster.getSampleModel(), + new Point(raster.getMinX(), + raster.getMinY())); + wr.setRect(raster); + + BufferedImage img2 = new BufferedImage(pc.getColorModel(), wr, + pc.getColorModel().isAlphaPremultiplied(), + null); + + setPaintPixels(nativePointer, + img2.getRGB(0, 0, deviceWidth, deviceHeight, null, 0, + deviceWidth), + deviceWidth, deviceHeight, deviceWidth, false, + deviceX, deviceY); + } + + // Restore transform + setTransformImpl(oldTx); } public Stroke getStroke() @@ -736,6 +865,33 @@ public abstract class CairoGraphics2D extends Graphics2D } } + /** + * Utility method to find the bounds of a shape, including the stroke width. + * + * @param s the shape + * @return the bounds of the shape, including stroke width + */ + protected Rectangle findStrokedBounds(Shape s) + { + Rectangle r = s.getBounds(); + + if (stroke instanceof BasicStroke) + { + int strokeWidth = (int)Math.ceil(((BasicStroke)stroke).getLineWidth()); + r.x -= strokeWidth / 2; + r.y -= strokeWidth / 2; + r.height += strokeWidth; + r.width += strokeWidth; + } + else + { + Shape s2 = stroke.createStrokedShape(s); + r = s2.getBounds(); + } + + return r; + } + public void setPaintMode() { setComposite(AlphaComposite.SrcOver); @@ -896,25 +1052,56 @@ public abstract class CairoGraphics2D extends Graphics2D */ public void setComposite(Composite comp) { + if (this.comp == comp) + return; + this.comp = comp; + if (compCtx != null) + compCtx.dispose(); + compCtx = null; if (comp instanceof AlphaComposite) { AlphaComposite a = (AlphaComposite) comp; - cairoSetOperator(nativePointer, a.getRule()); + cairoSetOperator(nativePointer, a.getRule()); } + else { - // FIXME: this check is only required "if this Graphics2D - // context is drawing to a Component on the display screen". - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - sm.checkPermission(new AWTPermission("readDisplayPixels")); - - // FIXME: implement general Composite support - throw new java.lang.UnsupportedOperationException(); + cairoSetOperator(nativePointer, AlphaComposite.SRC_OVER); + + if (comp != null) + { + // FIXME: this check is only required "if this Graphics2D + // context is drawing to a Component on the display screen". + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new AWTPermission("readDisplayPixels")); + + compCtx = comp.createContext(getBufferCM(), getNativeCM(), hints); + } } } + + /** + * Returns the Colour Model describing the native, raw image data for this + * specific peer. + * + * @return ColorModel the ColorModel of native data in this peer + */ + protected abstract ColorModel getNativeCM(); + + /** + * Returns the Color Model describing the buffer that this peer uses + * for custom composites. + * + * @return ColorModel the ColorModel of the composite buffer in this peer. + */ + protected ColorModel getBufferCM() + { + // This may be overridden by some subclasses + return getNativeCM(); + } ///////////////////////// DRAWING PRIMITIVES /////////////////////////////////// @@ -929,21 +1116,30 @@ public abstract class CairoGraphics2D extends Graphics2D return; } - createPath(s); + if (customPaint) + { + Rectangle r = findStrokedBounds(s); + setCustomPaint(r); + } + + createPath(s, true); cairoStroke(nativePointer); } public void fill(Shape s) { - createPath(s); + createPath(s, false); + if (customPaint) + setCustomPaint(s.getBounds()); + double alpha = 1.0; if (comp instanceof AlphaComposite) alpha = ((AlphaComposite) comp).getAlpha(); cairoFill(nativePointer, alpha); } - private void createPath(Shape s) + private void createPath(Shape s, boolean isDraw) { cairoNewPath(nativePointer); @@ -951,9 +1147,25 @@ public abstract class CairoGraphics2D extends Graphics2D if (s instanceof Rectangle2D) { Rectangle2D r = (Rectangle2D) s; - cairoRectangle(nativePointer, shifted(r.getX(), shiftDrawCalls), - shifted(r.getY(), shiftDrawCalls), r.getWidth(), - r.getHeight()); + + // Pixels need to be shifted in draw operations to ensure that they + // light up entire pixels, but we also need to make sure the rectangle + // does not get distorted by this shifting operation + double x = shiftX(r.getX(),shiftDrawCalls && isDraw); + double y = shiftY(r.getY(), shiftDrawCalls && isDraw); + double w = Math.round(r.getWidth()); + double h = Math.round(r.getHeight()); + cairoRectangle(nativePointer, x, y, w, h); + } + + // Lines are easy too + else if (s instanceof Line2D) + { + Line2D l = (Line2D) s; + cairoMoveTo(nativePointer, shiftX(l.getX1(), shiftDrawCalls && isDraw), + shiftY(l.getY1(), shiftDrawCalls && isDraw)); + cairoLineTo(nativePointer, shiftX(l.getX2(), shiftDrawCalls && isDraw), + shiftY(l.getY2(), shiftDrawCalls && isDraw)); } // We can optimize ellipses too; however we don't bother optimizing arcs: @@ -982,9 +1194,9 @@ public abstract class CairoGraphics2D extends Graphics2D } cairoArc(nativePointer, - shifted(e.getCenterX() / xscale, shiftDrawCalls), - shifted(e.getCenterY() / yscale, shiftDrawCalls), radius, 0, - Math.PI * 2); + shiftX(e.getCenterX() / xscale, shiftDrawCalls && isDraw), + shiftY(e.getCenterY() / yscale, shiftDrawCalls && isDraw), + radius, 0, Math.PI * 2); if (xscale != 1 || yscale != 1) cairoRestore(nativePointer); @@ -993,7 +1205,7 @@ public abstract class CairoGraphics2D extends Graphics2D // All other shapes are broken down and drawn in steps using the // PathIterator else - walkPath(s.getPathIterator(null), shiftDrawCalls); + walkPath(s.getPathIterator(null), shiftDrawCalls && isDraw); } /** @@ -1006,8 +1218,14 @@ public abstract class CairoGraphics2D extends Graphics2D { if (bg != null) cairoSetRGBAColor(nativePointer, bg.getRed() / 255.0, - bg.getGreen() / 255.0, bg.getBlue() / 255.0, 1.0); + bg.getGreen() / 255.0, bg.getBlue() / 255.0, + bg.getAlpha() / 255.0); + + Composite oldcomp = comp; + setComposite(AlphaComposite.Src); fillRect(x, y, width, height); + + setComposite(oldcomp); updateColor(); } @@ -1033,15 +1251,14 @@ public abstract class CairoGraphics2D extends Graphics2D // to draw a single pixel. This is emulated by drawing // a one pixel sized rectangle. if (x1 == x2 && y1 == y2) - cairoFillRect(nativePointer, x1, y1, 1, 1); + fill(new Rectangle(x1, y1, 1, 1)); else - cairoDrawLine(nativePointer, x1 + 0.5, y1 + 0.5, x2 + 0.5, y2 + 0.5); + draw(new Line2D.Double(x1, y1, x2, y2)); } public void drawRect(int x, int y, int width, int height) { - cairoDrawRect(nativePointer, shifted(x, shiftDrawCalls), - shifted(y, shiftDrawCalls), width, height); + draw(new Rectangle(x, y, width, height)); } public void fillArc(int x, int y, int width, int height, int startAngle, @@ -1049,12 +1266,12 @@ public abstract class CairoGraphics2D extends Graphics2D { fill(new Arc2D.Double((double) x, (double) y, (double) width, (double) height, (double) startAngle, - (double) arcAngle, Arc2D.OPEN)); + (double) arcAngle, Arc2D.PIE)); } public void fillRect(int x, int y, int width, int height) { - cairoFillRect(nativePointer, x, y, width, height); + fill (new Rectangle(x, y, width, height)); } public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) @@ -1117,7 +1334,7 @@ public abstract class CairoGraphics2D extends Graphics2D Rectangle2D r = getRealBounds(); - if( width < 0 || height < 0 ) + if( width <= 0 || height <= 0 ) return; // Return if outside the surface if( x + dx > r.getWidth() || y + dy > r.getHeight() ) @@ -1150,32 +1367,10 @@ public abstract class CairoGraphics2D extends Graphics2D ///////////////////////// RENDERING HINTS /////////////////////////////////// - /** - * FIXME- support better - */ public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) { hints.put(hintKey, hintValue); - if (hintKey.equals(RenderingHints.KEY_INTERPOLATION) - || hintKey.equals(RenderingHints.KEY_ALPHA_INTERPOLATION)) - { - if (hintValue.equals(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)) - cairoSurfaceSetFilter(nativePointer, 0); - - else if (hintValue.equals(RenderingHints.VALUE_INTERPOLATION_BILINEAR)) - cairoSurfaceSetFilter(nativePointer, 1); - - else if (hintValue.equals(RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED)) - cairoSurfaceSetFilter(nativePointer, 2); - - else if (hintValue.equals(RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY)) - cairoSurfaceSetFilter(nativePointer, 3); - - else if (hintValue.equals(RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT)) - cairoSurfaceSetFilter(nativePointer, 4); - } - shiftDrawCalls = hints.containsValue(RenderingHints.VALUE_STROKE_NORMALIZE) || hints.containsValue(RenderingHints.VALUE_STROKE_DEFAULT); } @@ -1189,30 +1384,15 @@ public abstract class CairoGraphics2D extends Graphics2D { this.hints = new RenderingHints(getDefaultHints()); this.hints.add(new RenderingHints(hints)); - - if (hints.containsKey(RenderingHints.KEY_INTERPOLATION)) - { - if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)) - cairoSurfaceSetFilter(nativePointer, 0); - - else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR)) - cairoSurfaceSetFilter(nativePointer, 1); - } - - if (hints.containsKey(RenderingHints.KEY_ALPHA_INTERPOLATION)) - { - if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED)) - cairoSurfaceSetFilter(nativePointer, 2); - - else if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY)) - cairoSurfaceSetFilter(nativePointer, 3); - - else if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT)) - cairoSurfaceSetFilter(nativePointer, 4); - } - + shiftDrawCalls = hints.containsValue(RenderingHints.VALUE_STROKE_NORMALIZE) || hints.containsValue(RenderingHints.VALUE_STROKE_DEFAULT); + + if (compCtx != null) + { + compCtx.dispose(); + compCtx = comp.createContext(getNativeCM(), getNativeCM(), this.hints); + } } public void addRenderingHints(Map hints) @@ -1224,6 +1404,30 @@ public abstract class CairoGraphics2D extends Graphics2D { return hints; } + + private int getInterpolation() + { + if (this.hints.containsValue(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)) + return INTERPOLATION_NEAREST; + + else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR)) + return INTERPOLATION_BILINEAR; + + else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BICUBIC)) + return INTERPOLATION_BICUBIC; + + else if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED)) + return ALPHA_INTERPOLATION_SPEED; + + else if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY)) + return ALPHA_INTERPOLATION_QUALITY; + + else if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT)) + return ALPHA_INTERPOLATION_DEFAULT; + + // Do bilinear interpolation as default + return INTERPOLATION_BILINEAR; + } ///////////////////////// IMAGE. METHODS /////////////////////////////////// @@ -1259,7 +1463,7 @@ public abstract class CairoGraphics2D extends Graphics2D // Note - this can get us in trouble when the gdk lock is re-acquired. // for example by VolatileImage. See ComponentGraphics for how we work // around this. - + img = AsyncImage.realImage(img, obs); if( !(img instanceof BufferedImage) ) { ImageProducer source = img.getSource(); @@ -1269,18 +1473,18 @@ public abstract class CairoGraphics2D extends Graphics2D } BufferedImage b = (BufferedImage) img; - DataBuffer db; + Raster raster; double[] i2u = new double[6]; int width = b.getWidth(); int height = b.getHeight(); - + // If this BufferedImage has a BufferedImageGraphics object, // use the cached CairoSurface that BIG is drawing onto if( BufferedImageGraphics.bufferedImages.get( b ) != null ) - db = (DataBuffer)BufferedImageGraphics.bufferedImages.get( b ); + raster = (Raster)BufferedImageGraphics.bufferedImages.get( b ); else - db = b.getRaster().getDataBuffer(); + raster = b.getRaster(); invertedXform.getMatrix(i2u); @@ -1288,29 +1492,37 @@ public abstract class CairoGraphics2D extends Graphics2D if (comp instanceof AlphaComposite) alpha = ((AlphaComposite) comp).getAlpha(); - if(db instanceof CairoSurface) + if(raster instanceof CairoSurface) { - ((CairoSurface)db).drawSurface(nativePointer, i2u, alpha); + ((CairoSurface)raster).drawSurface(nativePointer, i2u, alpha, + getInterpolation()); updateColor(); return true; } if( bgcolor != null ) { - // Fill a rectangle with the background color - // to composite the image onto. - Paint oldPaint = paint; - AffineTransform oldTransform = transform; - setPaint( bgcolor ); - setTransform( invertedXform ); - fillRect(0, 0, width, height); - setTransform( oldTransform ); - setPaint( oldPaint ); + Color oldColor = bg; + setBackground(bgcolor); + + double[] origin = new double[] {0,0}; + double[] dimensions = new double[] {width, height}; + xform.transform(origin, 0, origin, 0, 1); + xform.deltaTransform(dimensions, 0, dimensions, 0, 1); + clearRect((int)origin[0], (int)origin[1], + (int)dimensions[0], (int)dimensions[1]); + + setBackground(oldColor); } int[] pixels = b.getRGB(0, 0, width, height, null, 0, width); + + // FIXME: The above method returns data in the standard ARGB colorspace, + // meaning data should NOT be alpha pre-multiplied; however Cairo expects + // data to be premultiplied. - drawPixels(nativePointer, pixels, width, height, width, i2u, alpha); + drawPixels(nativePointer, pixels, width, height, width, i2u, alpha, + getInterpolation()); // Cairo seems to lose the current color which must be restored. updateColor(); @@ -1428,8 +1640,14 @@ public abstract class CairoGraphics2D extends Graphics2D { if (str == null || str.length() == 0) return; - (new TextLayout( str, getFont(), getFontRenderContext() )). - draw(this, x, y); + GdkFontPeer fontPeer = (GdkFontPeer) font.getPeer(); + TextLayout tl = (TextLayout) fontPeer.textLayoutCache.get(str); + if (tl == null) + { + tl = new TextLayout( str, getFont(), getFontRenderContext() ); + fontPeer.textLayoutCache.put(str, tl); + } + tl.draw(this, x, y); } public void drawString(String str, int x, int y) @@ -1449,6 +1667,9 @@ public abstract class CairoGraphics2D extends Graphics2D if( gv.getNumGlyphs() <= 0 ) return; + if (customPaint) + setCustomPaint(gv.getOutline().getBounds()); + if (comp instanceof AlphaComposite) alpha = ((AlphaComposite) comp).getAlpha(); if (gv instanceof FreetypeGlyphVector && alpha == 1.0) @@ -1458,9 +1679,10 @@ public abstract class CairoGraphics2D extends Graphics2D float[] positions = gv.getGlyphPositions (0, n, null); setFont (gv.getFont ()); - synchronized( this.font ) + GdkFontPeer fontPeer = (GdkFontPeer) font.getPeer(); + synchronized (fontPeer) { - cairoDrawGlyphVector(nativePointer, (GdkFontPeer)getFont().getPeer(), + cairoDrawGlyphVector(nativePointer, fontPeer, x, y, n, codes, positions); } } @@ -1498,9 +1720,7 @@ public abstract class CairoGraphics2D extends Graphics2D public FontMetrics getFontMetrics(Font f) { - // the reason we go via the toolkit here is to try to get - // a cached object. the toolkit keeps such a cache. - return Toolkit.getDefaultToolkit().getFontMetrics(f); + return ((GdkFontPeer) f.getPeer()).getFontMetrics(f); } public void setFont(Font f) @@ -1516,6 +1736,12 @@ public abstract class CairoGraphics2D extends Graphics2D font = ((ClasspathToolkit)(Toolkit.getDefaultToolkit())) .getFont(f.getName(), f.getAttributes()); + + GdkFontPeer fontpeer = (GdkFontPeer) getFont().getPeer(); + synchronized (fontpeer) + { + cairoSetFont(nativePointer, fontpeer); + } } public Font getFont() @@ -1626,7 +1852,7 @@ public abstract class CairoGraphics2D extends Graphics2D if (comp instanceof AlphaComposite) alpha = ((AlphaComposite) comp).getAlpha(); drawPixels(nativePointer, pixels, r.getWidth(), r.getHeight(), - r.getWidth(), i2u, alpha); + r.getWidth(), i2u, alpha, getInterpolation()); // Cairo seems to lose the current color which must be restored. updateColor(); @@ -1635,12 +1861,33 @@ public abstract class CairoGraphics2D extends Graphics2D } /** - * Shifts coordinates by 0.5. + * Shifts an x-coordinate by 0.5 in device space. + */ + private double shiftX(double coord, boolean doShift) + { + if (doShift) + { + double shift = 0.5; + if (!transform.isIdentity()) + shift /= transform.getScaleX(); + return Math.round(coord) + shift; + } + else + return coord; + } + + /** + * Shifts a y-coordinate by 0.5 in device space. */ - private double shifted(double coord, boolean doShift) + private double shiftY(double coord, boolean doShift) { if (doShift) - return Math.floor(coord) + 0.5; + { + double shift = 0.5; + if (!transform.isIdentity()) + shift /= transform.getScaleY(); + return Math.round(coord) + shift; + } else return coord; } @@ -1661,35 +1908,35 @@ public abstract class CairoGraphics2D extends Graphics2D switch (seg) { case PathIterator.SEG_MOVETO: - x = shifted(coords[0], doShift); - y = shifted(coords[1], doShift); + x = shiftX(coords[0], doShift); + y = shiftY(coords[1], doShift); cairoMoveTo(nativePointer, x, y); break; case PathIterator.SEG_LINETO: - x = shifted(coords[0], doShift); - y = shifted(coords[1], doShift); + x = shiftX(coords[0], doShift); + y = shiftY(coords[1], doShift); cairoLineTo(nativePointer, x, y); break; case PathIterator.SEG_QUADTO: // splitting a quadratic bezier into a cubic: // see: http://pfaedit.sourceforge.net/bezier.html - double x1 = x + (2.0 / 3.0) * (shifted(coords[0], doShift) - x); - double y1 = y + (2.0 / 3.0) * (shifted(coords[1], doShift) - y); + double x1 = x + (2.0 / 3.0) * (shiftX(coords[0], doShift) - x); + double y1 = y + (2.0 / 3.0) * (shiftY(coords[1], doShift) - y); - double x2 = x1 + (1.0 / 3.0) * (shifted(coords[2], doShift) - x); - double y2 = y1 + (1.0 / 3.0) * (shifted(coords[3], doShift) - y); + double x2 = x1 + (1.0 / 3.0) * (shiftX(coords[2], doShift) - x); + double y2 = y1 + (1.0 / 3.0) * (shiftY(coords[3], doShift) - y); - x = shifted(coords[2], doShift); - y = shifted(coords[3], doShift); + x = shiftX(coords[2], doShift); + y = shiftY(coords[3], doShift); cairoCurveTo(nativePointer, x1, y1, x2, y2, x, y); break; case PathIterator.SEG_CUBICTO: - x = shifted(coords[4], doShift); - y = shifted(coords[5], doShift); - cairoCurveTo(nativePointer, shifted(coords[0], doShift), - shifted(coords[1], doShift), - shifted(coords[2], doShift), - shifted(coords[3], doShift), x, y); + x = shiftX(coords[4], doShift); + y = shiftY(coords[5], doShift); + cairoCurveTo(nativePointer, shiftX(coords[0], doShift), + shiftY(coords[1], doShift), + shiftX(coords[2], doShift), + shiftY(coords[3], doShift), x, y); break; case PathIterator.SEG_CLOSE: cairoClosePath(nativePointer); @@ -1807,4 +2054,4 @@ public abstract class CairoGraphics2D extends Graphics2D return rect; } -} +}
\ No newline at end of file diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/CairoSurface.java b/libjava/classpath/gnu/java/awt/peer/gtk/CairoSurface.java index 78bc1e02db0..5b63e62e7ed 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/CairoSurface.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/CairoSurface.java @@ -38,14 +38,18 @@ exception statement from your version. */ package gnu.java.awt.peer.gtk; -import java.awt.Point; +import gnu.java.awt.Buffers; + import java.awt.Graphics2D; -import java.awt.image.DataBuffer; -import java.awt.image.Raster; -import java.awt.image.WritableRaster; +import java.awt.Point; +import java.awt.color.ColorSpace; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; import java.awt.image.DirectColorModel; +import java.awt.image.SampleModel; +import java.awt.image.SinglePixelPackedSampleModel; +import java.awt.image.WritableRaster; import java.nio.ByteOrder; import java.util.Hashtable; @@ -54,7 +58,7 @@ import java.util.Hashtable; * * @author Sven de Marothy */ -public class CairoSurface extends DataBuffer +public class CairoSurface extends WritableRaster { int width = -1, height = -1; @@ -68,13 +72,27 @@ public class CairoSurface extends DataBuffer */ long bufferPointer; - - static ColorModel nativeModel = new DirectColorModel(32, - 0x00FF0000, - 0x0000FF00, - 0x000000FF, - 0xFF000000); - + // FIXME: use only the cairoCM_pre colormodel + // since that's what Cairo really uses (is there a way to do this cheaply? + // we use a non-multiplied model most of the time to avoid costly coercion + // operations...) + static ColorModel cairoColorModel = new DirectColorModel(32, 0x00FF0000, + 0x0000FF00, + 0x000000FF, + 0xFF000000); + + static ColorModel cairoCM_pre = new DirectColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), + 32, 0x00FF0000, + 0x0000FF00, + 0x000000FF, + 0xFF000000, + true, + Buffers.smallestAppropriateTransferType(32)); + + // This CM corresponds to the CAIRO_FORMAT_RGB24 type in Cairo + static ColorModel cairoCM_opaque = new DirectColorModel(24, 0x00FF0000, + 0x0000FF00, + 0x000000FF); /** * Allocates and clears the buffer and creates the cairo surface. * @param width, height - the image size @@ -102,11 +120,13 @@ public class CairoSurface extends DataBuffer * with an affine transform given by i2u. */ public native void nativeDrawSurface(long surfacePointer, long contextPointer, - double[] i2u, double alpha); + double[] i2u, double alpha, + int interpolation); - public void drawSurface(long contextPointer, double[] i2u, double alpha) + public void drawSurface(long contextPointer, double[] i2u, double alpha, + int interpolation) { - nativeDrawSurface(surfacePointer, contextPointer, i2u, alpha); + nativeDrawSurface(surfacePointer, contextPointer, i2u, alpha, interpolation); } /** @@ -138,18 +158,20 @@ public class CairoSurface extends DataBuffer */ public CairoSurface(int width, int height) { - super(DataBuffer.TYPE_INT, width * height); + super(createCairoSampleModel(width, height), + null, new Point(0, 0)); if(width <= 0 || height <= 0) throw new IllegalArgumentException("Image must be at least 1x1 pixels."); - + this.width = width; this.height = height; - create(width, height, width); if(surfacePointer == 0 || bufferPointer == 0) throw new Error("Could not allocate bitmap."); + + dataBuffer = new CairoDataBuffer(); } /** @@ -158,18 +180,7 @@ public class CairoSurface extends DataBuffer */ CairoSurface(GtkImage image) { - super(DataBuffer.TYPE_INT, image.width * image.height); - - if(image.width <= 0 || image.height <= 0) - throw new IllegalArgumentException("Image must be at least 1x1 pixels."); - - width = image.width; - height = image.height; - - create(width, height, width); - - if(surfacePointer == 0 || bufferPointer == 0) - throw new Error("Could not allocate bitmap."); + this(image.width, image.height); // Copy the pixel data from the GtkImage. int[] data = image.getPixels(); @@ -260,32 +271,37 @@ public class CairoSurface extends DataBuffer */ public static BufferedImage getBufferedImage(CairoSurface surface) { - WritableRaster raster = Raster.createPackedRaster - (surface, surface.width, surface.height, surface.width, - new int[]{ 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000 }, - new Point(0,0)); - - return new BufferedImage(nativeModel, raster, true, new Hashtable()); + return new BufferedImage(cairoColorModel, surface, + cairoColorModel.isAlphaPremultiplied(), + new Hashtable()); } - /** - * DataBank.getElem implementation - */ - public int getElem(int bank, int i) + private class CairoDataBuffer extends DataBuffer { - if(bank != 0 || i < 0 || i >= width*height) - throw new IndexOutOfBoundsException(i+" size: "+width*height); - return nativeGetElem(bufferPointer, i); - } + public CairoDataBuffer() + { + super(DataBuffer.TYPE_INT, width * height); + } + + /** + * DataBuffer.getElem implementation + */ + public int getElem(int bank, int i) + { + if(bank != 0 || i < 0 || i >= width * height) + throw new IndexOutOfBoundsException(i+" size: "+width * height); + return nativeGetElem(bufferPointer, i); + } - /** - * DataBank.setElem implementation - */ - public void setElem(int bank, int i, int val) - { - if(bank != 0 || i < 0 || i >= width*height) - throw new IndexOutOfBoundsException(i+" size: "+width*height); - nativeSetElem(bufferPointer, i, val); + /** + * DataBuffer.setElem implementation + */ + public void setElem(int bank, int i, int val) + { + if(bank != 0 || i < 0 || i >= width*height) + throw new IndexOutOfBoundsException(i+" size: "+width * height); + nativeSetElem(bufferPointer, i, val); + } } /** @@ -319,4 +335,14 @@ public class CairoSurface extends DataBuffer { copyAreaNative2(bufferPointer, x, y, width, height, dx, dy, stride); } + + /** + * Creates a SampleModel that matches Cairo's native format + */ + protected static SampleModel createCairoSampleModel(int w, int h) + { + return new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, w, h, + new int[]{0x00FF0000, 0x0000FF00, + 0x000000FF, 0xFF000000}); + } } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/CairoSurfaceGraphics.java b/libjava/classpath/gnu/java/awt/peer/gtk/CairoSurfaceGraphics.java index 7bd136c3845..36743b9c2da 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/CairoSurfaceGraphics.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/CairoSurfaceGraphics.java @@ -38,10 +38,26 @@ exception statement from your version. */ package gnu.java.awt.peer.gtk; +import java.awt.AlphaComposite; +import java.awt.Color; import java.awt.Graphics; -import java.awt.GraphicsEnvironment; +import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.Toolkit; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.image.RenderedImage; +import java.util.Hashtable; /** * Implementation of Graphics2D on a Cairo surface. @@ -49,6 +65,7 @@ import java.awt.geom.Rectangle2D; public class CairoSurfaceGraphics extends CairoGraphics2D { protected CairoSurface surface; + private BufferedImage buffer; private long cairo_t; /** @@ -59,6 +76,7 @@ public class CairoSurfaceGraphics extends CairoGraphics2D this.surface = surface; cairo_t = surface.newCairoContext(); setup( cairo_t ); + setClip(0, 0, surface.width, surface.height); } /** @@ -91,4 +109,200 @@ public class CairoSurfaceGraphics extends CairoGraphics2D { surface.copyAreaNative(x, y, width, height, dx, dy, surface.width); } + + /** + * Overloaded methods that do actual drawing need to account for custom + * composites + */ + public void draw(Shape s) + { + if (comp == null || comp instanceof AlphaComposite) + super.draw(s); + + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setStroke(this.getStroke()); + g2d.setColor(this.getColor()); + g2d.draw(s); + + drawComposite(s.getBounds2D(), null); + } + } + + public void fill(Shape s) + { + if (comp == null || comp instanceof AlphaComposite) + super.fill(s); + + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setPaint(this.getPaint()); + g2d.setColor(this.getColor()); + g2d.fill(s); + + drawComposite(s.getBounds2D(), null); + } + } + + public void drawRenderedImage(RenderedImage image, AffineTransform xform) + { + if (comp == null || comp instanceof AlphaComposite) + super.drawRenderedImage(image, xform); + + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setRenderingHints(this.getRenderingHints()); + g2d.drawRenderedImage(image, xform); + + drawComposite(buffer.getRaster().getBounds(), null); + } + + } + + protected boolean drawImage(Image img, AffineTransform xform, + Color bgcolor, ImageObserver obs) + { + if (comp == null || comp instanceof AlphaComposite) + return super.drawImage(img, xform, bgcolor, obs); + + else + { + // Get buffered image of source + if( !(img instanceof BufferedImage) ) + { + ImageProducer source = img.getSource(); + if (source == null) + return false; + img = Toolkit.getDefaultToolkit().createImage(source); + } + BufferedImage bImg = (BufferedImage) img; + + // Find translated bounds + Point2D origin = new Point2D.Double(bImg.getMinX(), bImg.getMinY()); + Point2D pt = new Point2D.Double(bImg.getWidth() + bImg.getMinX(), + bImg.getHeight() + bImg.getMinY()); + if (xform != null) + { + origin = xform.transform(origin, origin); + pt = xform.transform(pt, pt); + } + + // Create buffer and draw image + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setRenderingHints(this.getRenderingHints()); + g2d.drawImage(img, xform, obs); + + // Perform compositing + return drawComposite(new Rectangle2D.Double(origin.getX(), + origin.getY(), + pt.getX(), pt.getY()), + obs); + } + } + + public void drawGlyphVector(GlyphVector gv, float x, float y) + { + if (comp == null || comp instanceof AlphaComposite) + super.drawGlyphVector(gv, x, y); + + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setPaint(this.getPaint()); + g2d.setStroke(this.getStroke()); + g2d.drawGlyphVector(gv, x, y); + + Rectangle2D bounds = gv.getLogicalBounds(); + bounds = new Rectangle2D.Double(x + bounds.getX(), y + bounds.getY(), + bounds.getWidth(), bounds.getHeight()); + drawComposite(bounds, null); + } + } + + private boolean drawComposite(Rectangle2D bounds, ImageObserver observer) + { + // Clip source to visible areas that need updating + Rectangle2D clip = this.getClipBounds(); + Rectangle2D.intersect(bounds, clip, bounds); + clip = new Rectangle(buffer.getMinX(), buffer.getMinY(), + buffer.getWidth(), buffer.getHeight()); + Rectangle2D.intersect(bounds, clip, bounds); + + BufferedImage buffer2 = buffer; + if (!bounds.equals(buffer2.getRaster().getBounds())) + buffer2 = buffer2.getSubimage((int)bounds.getX(), (int)bounds.getY(), + (int)bounds.getWidth(), + (int)bounds.getHeight()); + + // Get destination clip to bounds + double[] points = new double[] {bounds.getX(), bounds.getY(), + bounds.getMaxX(), bounds.getMaxY()}; + transform.transform(points, 0, points, 0, 2); + + Rectangle2D deviceBounds = new Rectangle2D.Double(points[0], points[1], + points[2] - points[0], + points[3] - points[1]); + + Rectangle2D.intersect(deviceBounds, this.getClipInDevSpace(), deviceBounds); + + BufferedImage current = CairoSurface.getBufferedImage(surface); + current = current.getSubimage((int)deviceBounds.getX(), + (int)deviceBounds.getY(), + (int)deviceBounds.getWidth(), + (int)deviceBounds.getHeight()); + + // Perform actual composite operation + compCtx.compose(buffer2.getRaster(), current.getRaster(), + buffer2.getRaster()); + + // This MUST call directly into the "action" method in CairoGraphics2D, + // not one of the wrappers, to ensure that the composite isn't processed + // more than once! + boolean rv = super.drawImage(buffer2, + AffineTransform.getTranslateInstance(bounds.getX(), + bounds.getY()), + new Color(0,0,0,0), null); + return rv; + } + + private void createBuffer() + { + if (buffer == null) + { + buffer = new BufferedImage(getBufferCM(), + surface.createCompatibleWritableRaster(), + getBufferCM().isAlphaPremultiplied(), + new Hashtable()); + } + else + { + Graphics2D g2d = ((Graphics2D)buffer.getGraphics()); + + g2d.setBackground(new Color(0,0,0,0)); + g2d.clearRect(0, 0, buffer.getWidth(), buffer.getHeight()); + } + } + + protected ColorModel getNativeCM() + { + return CairoSurface.cairoCM_pre; + } + + protected ColorModel getBufferCM() + { + return CairoSurface.cairoColorModel; + } } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/ComponentGraphics.java b/libjava/classpath/gnu/java/awt/peer/gtk/ComponentGraphics.java index ffa78e9c904..763ad7dfddd 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/ComponentGraphics.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/ComponentGraphics.java @@ -38,22 +38,31 @@ exception statement from your version. */ package gnu.java.awt.peer.gtk; +import gnu.classpath.Pointer; + +import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.Image; +import java.awt.Point; import java.awt.Rectangle; import java.awt.Shape; import java.awt.Toolkit; import java.awt.font.GlyphVector; import java.awt.geom.AffineTransform; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; import java.awt.image.ImageObserver; import java.awt.image.ImageProducer; +import java.awt.image.Raster; import java.awt.image.RenderedImage; -import gnu.classpath.Pointer; +import java.awt.image.WritableRaster; +import java.util.Hashtable; /** * ComponentGraphics - context for drawing directly to a component, @@ -67,36 +76,11 @@ public class ComponentGraphics extends CairoGraphics2D private GtkComponentPeer component; protected long cairo_t; + private BufferedImage buffer, componentBuffer; private static ThreadLocal hasLock = new ThreadLocal(); private static Integer ONE = Integer.valueOf(1); - private void lock() - { - Integer i = (Integer) hasLock.get(); - if (i == null) - { - start_gdk_drawing(); - hasLock.set(ONE); - } - else - hasLock.set(Integer.valueOf(i.intValue() + 1)); - } - - private void unlock() - { - Integer i = (Integer) hasLock.get(); - if (i == null) - throw new IllegalStateException(); - if (i == ONE) - { - hasLock.set(null); - end_gdk_drawing(); - } - else - hasLock.set(Integer.valueOf(i.intValue() - 1)); - } - ComponentGraphics() { } @@ -128,6 +112,32 @@ public class ComponentGraphics extends CairoGraphics2D */ private native long initState(GtkComponentPeer component); + private void lock() + { + Integer i = (Integer) hasLock.get(); + if (i == null) + { + start_gdk_drawing(); + hasLock.set(ONE); + } + else + hasLock.set(Integer.valueOf(i.intValue() + 1)); + } + + private void unlock() + { + Integer i = (Integer) hasLock.get(); + if (i == null) + throw new IllegalStateException(); + if (i == ONE) + { + hasLock.set(null); + end_gdk_drawing(); + } + else + hasLock.set(Integer.valueOf(i.intValue() - 1)); + } + /** * Destroys the component surface and calls dispose on the cairo * graphics2d to destroy any super class resources. @@ -227,7 +237,20 @@ public class ComponentGraphics extends CairoGraphics2D lock(); try { - super.draw(s); + if (comp == null || comp instanceof AlphaComposite) + super.draw(s); + + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setStroke(this.getStroke()); + g2d.setColor(this.getColor()); + g2d.draw(s); + + drawComposite(s.getBounds2D(), null); + } } finally { @@ -240,7 +263,20 @@ public class ComponentGraphics extends CairoGraphics2D lock(); try { - super.fill(s); + if (comp == null || comp instanceof AlphaComposite) + super.fill(s); + + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setPaint(this.getPaint()); + g2d.setColor(this.getColor()); + g2d.fill(s); + + drawComposite(s.getBounds2D(), null); + } } finally { @@ -253,7 +289,19 @@ public class ComponentGraphics extends CairoGraphics2D lock(); try { - super.drawRenderedImage(image, xform); + if (comp == null || comp instanceof AlphaComposite) + super.drawRenderedImage(image, xform); + + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setRenderingHints(this.getRenderingHints()); + g2d.drawRenderedImage(image, xform); + + drawComposite(buffer.getRaster().getBounds(), null); + } } finally { @@ -268,7 +316,44 @@ public class ComponentGraphics extends CairoGraphics2D lock(); try { - rv = super.drawImage(img, xform, bgcolor, obs); + if (comp == null || comp instanceof AlphaComposite) + rv = super.drawImage(img, xform, bgcolor, obs); + + else + { + // Get buffered image of source + if( !(img instanceof BufferedImage) ) + { + ImageProducer source = img.getSource(); + if (source == null) + return false; + img = Toolkit.getDefaultToolkit().createImage(source); + } + BufferedImage bImg = (BufferedImage) img; + + // Find translated bounds + Point2D origin = new Point2D.Double(bImg.getMinX(), bImg.getMinY()); + Point2D pt = new Point2D.Double(bImg.getWidth() + bImg.getMinX(), + bImg.getHeight() + bImg.getMinY()); + if (xform != null) + { + origin = xform.transform(origin, origin); + pt = xform.transform(pt, pt); + } + + // Create buffer and draw image + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setRenderingHints(this.getRenderingHints()); + g2d.drawImage(img, xform, obs); + + // Perform compositing + rv = drawComposite(new Rectangle2D.Double(origin.getX(), + origin.getY(), + pt.getX(), pt.getY()), + obs); + } } finally { @@ -282,7 +367,23 @@ public class ComponentGraphics extends CairoGraphics2D lock(); try { - super.drawGlyphVector(gv, x, y); + if (comp == null || comp instanceof AlphaComposite) + super.drawGlyphVector(gv, x, y); + + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setPaint(this.getPaint()); + g2d.setStroke(this.getStroke()); + g2d.drawGlyphVector(gv, x, y); + + Rectangle2D bounds = gv.getLogicalBounds(); + bounds = new Rectangle2D.Double(x + bounds.getX(), y + bounds.getY(), + bounds.getWidth(), bounds.getHeight()); + drawComposite(bounds, null); + } } finally { @@ -367,6 +468,7 @@ public class ComponentGraphics extends CairoGraphics2D } BufferedImage bimg; + img = AsyncImage.realImage(img, observer); if (img instanceof BufferedImage) bimg = (BufferedImage) img; else @@ -379,57 +481,112 @@ public class ComponentGraphics extends CairoGraphics2D return super.drawImage(bimg, x, y, width, height, observer); } - public void drawLine(int x1, int y1, int x2, int y2) + public void setClip(Shape s) { lock(); try { - super.drawLine(x1, y1, x2, y2); + super.setClip(s); } finally { - unlock(); + unlock(); } } - public void drawRect(int x, int y, int width, int height) + + private boolean drawComposite(Rectangle2D bounds, ImageObserver observer) { + // Clip source to visible areas that need updating + Rectangle2D clip = this.getClipBounds(); + Rectangle2D.intersect(bounds, clip, bounds); + clip = new Rectangle(buffer.getMinX(), buffer.getMinY(), + buffer.getWidth(), buffer.getHeight()); + Rectangle2D.intersect(bounds, clip, bounds); + + BufferedImage buffer2 = buffer; + if (!bounds.equals(buffer2.getRaster().getBounds())) + buffer2 = buffer2.getSubimage((int)bounds.getX(), (int)bounds.getY(), + (int)bounds.getWidth(), + (int)bounds.getHeight()); + + // Get destination clip to bounds + double[] points = new double[] {bounds.getX(), bounds.getY(), + bounds.getMaxX(), bounds.getMaxY()}; + transform.transform(points, 0, points, 0, 2); + + Rectangle2D deviceBounds = new Rectangle2D.Double(points[0], points[1], + points[2] - points[0], + points[3] - points[1]); + + Rectangle2D.intersect(deviceBounds, this.getClipInDevSpace(), deviceBounds); + + // Get current image on the component + unlock(); + GtkImage img = grab(component); + Graphics gr = componentBuffer.createGraphics(); + gr.drawImage(img, 0, 0, null); + gr.dispose(); lock(); - try - { - super.drawRect(x, y, width, height); - } - finally - { - unlock(); - } + + BufferedImage cBuffer = componentBuffer; + if (!deviceBounds.equals(cBuffer.getRaster().getBounds())) + cBuffer = cBuffer.getSubimage((int)deviceBounds.getX(), + (int)deviceBounds.getY(), + (int)deviceBounds.getWidth(), + (int)deviceBounds.getHeight()); + + // Perform actual composite operation + compCtx.compose(buffer2.getRaster(), cBuffer.getRaster(), + cBuffer.getRaster()); + + // This MUST call directly into the "action" method in CairoGraphics2D, + // not one of the wrappers, to ensure that the composite isn't processed + // more than once! + boolean rv = super.drawImage(cBuffer, + AffineTransform.getTranslateInstance(bounds.getX(), + bounds.getY()), + null, null); + return rv; } - - public void fillRect(int x, int y, int width, int height) + + private void createBuffer() { - lock(); - try - { - super.fillRect(x, y, width, height); - } - finally + if (buffer == null) { - unlock(); + WritableRaster rst; + rst = Raster.createWritableRaster(GtkVolatileImage.createGdkSampleModel(component.awtComponent.getWidth(), + component.awtComponent.getHeight()), + new Point(0,0)); + + buffer = new BufferedImage(GtkVolatileImage.gdkColorModel, rst, + GtkVolatileImage.gdkColorModel.isAlphaPremultiplied(), + new Hashtable()); } - } - - public void setClip(Shape s) - { - lock(); - try + else { - super.setClip(s); + Graphics2D g2d = ((Graphics2D)buffer.getGraphics()); + + g2d.setBackground(new Color(0,0,0,0)); + g2d.clearRect(0, 0, buffer.getWidth(), buffer.getHeight()); } - finally + + if (componentBuffer == null) { - unlock(); + WritableRaster rst; + rst = Raster.createWritableRaster(GtkVolatileImage.createGdkSampleModel(component.awtComponent.getWidth(), + component.awtComponent.getHeight()), + new Point(0,0)); + + componentBuffer = new BufferedImage(GtkVolatileImage.gdkColorModel, rst, + GtkVolatileImage.gdkColorModel.isAlphaPremultiplied(), + new Hashtable()); } } - + + protected ColorModel getNativeCM() + { + return GtkVolatileImage.gdkColorModel; + } } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/FreetypeGlyphVector.java b/libjava/classpath/gnu/java/awt/peer/gtk/FreetypeGlyphVector.java index 2c9d917934f..131a964488f 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/FreetypeGlyphVector.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/FreetypeGlyphVector.java @@ -39,14 +39,15 @@ package gnu.java.awt.peer.gtk; import java.awt.Font; import java.awt.Shape; -import java.awt.geom.AffineTransform; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.awt.geom.GeneralPath; +import java.awt.font.FontRenderContext; import java.awt.font.GlyphJustificationInfo; import java.awt.font.GlyphMetrics; import java.awt.font.GlyphVector; -import java.awt.font.FontRenderContext; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.Arrays; public class FreetypeGlyphVector extends GlyphVector { @@ -91,16 +92,17 @@ public class FreetypeGlyphVector extends GlyphVector */ public FreetypeGlyphVector(Font f, String s, FontRenderContext frc) { - this(f, s, frc, Font.LAYOUT_LEFT_TO_RIGHT); + this(f, s.toCharArray(), 0, s.length(), frc, Font.LAYOUT_LEFT_TO_RIGHT); } /** * Create a glyphvector from a given (Freetype) font and a String. */ - public FreetypeGlyphVector(Font f, String s, FontRenderContext frc, - int flags) + public FreetypeGlyphVector(Font f, char[] chars, int start, int len, + FontRenderContext frc, int flags) { - this.s = s; + this.s = new String(chars, start, len); + this.font = f; this.frc = frc; if( !(font.getPeer() instanceof GdkFontPeer ) ) @@ -155,14 +157,15 @@ public class FreetypeGlyphVector extends GlyphVector } glyphCodes = new int[ nGlyphs ]; - glyphPositions = new float[ nGlyphs ]; + glyphPositions = new float[(nGlyphs + 1) * 2]; glyphTransforms = new AffineTransform[ nGlyphs ]; for(int i = 0; i < nGlyphs; i++ ) { - glyphTransforms[ i ] = new AffineTransform( gv.glyphTransforms[ i ] ); - glyphCodes[i] = gv.glyphCodes[ i ]; - glyphPositions[i] = gv.glyphPositions[ i ]; + glyphTransforms[ i ] = new AffineTransform( gv.glyphTransforms[ i ] ); + glyphCodes[i] = gv.glyphCodes[ i ]; } + System.arraycopy(gv.glyphPositions, 0, glyphPositions, 0, + glyphPositions.length); } /** @@ -178,10 +181,17 @@ public class FreetypeGlyphVector extends GlyphVector for(int i = 0; i < nGlyphs; i++) { codePoints[i] = s.codePointAt( stringIndex ); - // UTF32 surrogate handling + // UTF32 surrogate handling if( codePoints[i] != (int)s.charAt( stringIndex ) ) stringIndex ++; stringIndex ++; + + if (Character.isISOControl(codePoints[i])) + { + // Replace with 'hair space'. Should better be 'zero-width space' + // but that doesn't seem to be supported by default font. + codePoints[i] = 8202; + } } glyphCodes = getGlyphs( codePoints ); @@ -242,22 +252,31 @@ public class FreetypeGlyphVector extends GlyphVector public void performDefaultLayout() { logicalBounds = null; // invalidate caches. - glyphPositions = null; - - glyphTransforms = new AffineTransform[ nGlyphs ]; - double x = 0; + glyphTransforms = new AffineTransform[nGlyphs]; + Arrays.fill(glyphTransforms, null); + glyphPositions = new float[(nGlyphs + 1) * 2]; + GlyphMetrics gm = null; + float x = 0; + float y = 0; for(int i = 0; i < nGlyphs; i++) { - GlyphMetrics gm = getGlyphMetrics( i ); - glyphTransforms[ i ] = AffineTransform.getTranslateInstance(x, 0); - x += gm.getAdvanceX(); - if( i > 0 ) - { - Point2D p = getKerning( glyphCodes[ i - 1 ], glyphCodes[ i ] ); - x += p.getX(); - } + gm = getGlyphMetrics( i ); + glyphPositions[i*2] = x; + glyphPositions[i*2 + 1] = y; + + x += gm.getAdvanceX(); + y += gm.getAdvanceY(); + + if (i != nGlyphs-1) + { + Point2D p = getKerning(glyphCodes[i], glyphCodes[i + 1]); + x += p.getX(); + y += p.getY(); + } } + glyphPositions[nGlyphs * 2] = x; + glyphPositions[nGlyphs * 2 + 1] = y; } /** @@ -276,7 +295,7 @@ public class FreetypeGlyphVector extends GlyphVector { int[] rval; - if( codeReturn == null ) + if( codeReturn == null || codeReturn.length < numEntries) rval = new int[ numEntries ]; else rval = codeReturn; @@ -286,9 +305,6 @@ public class FreetypeGlyphVector extends GlyphVector return rval; } - /** - * FIXME: Implement me. - */ public Shape getGlyphLogicalBounds(int glyphIndex) { GlyphMetrics gm = getGlyphMetrics( glyphIndex ); @@ -296,10 +312,17 @@ public class FreetypeGlyphVector extends GlyphVector return null; Rectangle2D r = gm.getBounds2D(); Point2D p = getGlyphPosition( glyphIndex ); - return new Rectangle2D.Double( p.getX() + r.getX() - gm.getLSB(), - p.getY() + r.getY(), - gm.getAdvanceX(), - r.getHeight() ); + + double[] bounds = new double[] {p.getX() + r.getX() - gm.getLSB(), + p.getY() + r.getY(), + p.getX() + r.getX() - gm.getLSB() + gm.getAdvanceX(), + p.getY() + r.getY() + r.getHeight()}; + + if (glyphTransforms[glyphIndex] != null) + glyphTransforms[glyphIndex].transform(bounds, 0, bounds, 0, 4); + + return new Rectangle2D.Double(bounds[0], bounds[1], bounds[2] - bounds[0], + bounds[3] - bounds[1]); } /* @@ -352,7 +375,9 @@ public class FreetypeGlyphVector extends GlyphVector public Shape getGlyphOutline(int glyphIndex) { GeneralPath gp = getGlyphOutlineNative( glyphCodes[ glyphIndex ] ); - gp.transform( glyphTransforms[ glyphIndex ] ); + if (glyphTransforms[glyphIndex] != null) + gp.transform( glyphTransforms[glyphIndex]); + return gp; } @@ -361,8 +386,8 @@ public class FreetypeGlyphVector extends GlyphVector */ public Point2D getGlyphPosition(int glyphIndex) { - return glyphTransforms[ glyphIndex ].transform( new Point2D.Double(0, 0), - null ); + return new Point2D.Float(glyphPositions[glyphIndex*2], + glyphPositions[glyphIndex*2 + 1]); } /** @@ -371,25 +396,12 @@ public class FreetypeGlyphVector extends GlyphVector public float[] getGlyphPositions(int beginGlyphIndex, int numEntries, float[] positionReturn) { - if( glyphPositions != null ) - return glyphPositions; - - float[] rval; - - if( positionReturn == null ) - rval = new float[2 * numEntries]; - else - rval = positionReturn; - - for( int i = beginGlyphIndex; i < numEntries; i++ ) - { - Point2D p = getGlyphPosition( i ); - rval[i * 2] = (float)p.getX(); - rval[i * 2 + 1] = (float)p.getY(); - } - - glyphPositions = rval; - return rval; + if (positionReturn == null || positionReturn.length < (numEntries * 2)) + positionReturn = new float[numEntries*2]; + + System.arraycopy(glyphPositions, beginGlyphIndex*2, positionReturn, 0, + numEntries*2); + return positionReturn; } /** @@ -397,7 +409,7 @@ public class FreetypeGlyphVector extends GlyphVector */ public AffineTransform getGlyphTransform(int glyphIndex) { - return new AffineTransform( glyphTransforms[ glyphIndex ] ); + return glyphTransforms[glyphIndex]; } /** @@ -420,10 +432,12 @@ public class FreetypeGlyphVector extends GlyphVector return logicalBounds; Rectangle2D rect = (Rectangle2D)getGlyphLogicalBounds( 0 ); + AffineTransform tx = new AffineTransform(); for( int i = 1; i < nGlyphs; i++ ) { - Rectangle2D r2 = (Rectangle2D)getGlyphLogicalBounds( i ); - rect = rect.createUnion( r2 ); + Rectangle2D r2 = (Rectangle2D)getGlyphLogicalBounds( i ); + + rect = rect.createUnion( r2 ); } logicalBounds = rect; @@ -444,8 +458,14 @@ public class FreetypeGlyphVector extends GlyphVector public Shape getOutline() { GeneralPath path = new GeneralPath(); + AffineTransform tx = new AffineTransform(); for( int i = 0; i < getNumGlyphs(); i++ ) - path.append( getGlyphOutline( i ), false ); + { + Shape outline = getGlyphOutline(i); + tx.setToTranslation(glyphPositions[i*2], glyphPositions[i*2 +1]); + outline = tx.createTransformedShape(outline); + path.append(outline, false); + } return path; } @@ -485,11 +505,9 @@ public class FreetypeGlyphVector extends GlyphVector */ public void setGlyphPosition(int glyphIndex, Point2D newPos) { - // FIXME: Scaling, etc.? - glyphTransforms[ glyphIndex ].setToTranslation( newPos.getX(), - newPos.getY() ); + glyphPositions[glyphIndex*2] = (float)(newPos.getX()); + glyphPositions[glyphIndex*2 + 1] = (float)(newPos.getY()); logicalBounds = null; - glyphPositions = null; } /** @@ -497,8 +515,7 @@ public class FreetypeGlyphVector extends GlyphVector */ public void setGlyphTransform(int glyphIndex, AffineTransform newTX) { - glyphTransforms[ glyphIndex ].setTransform( newTX ); logicalBounds = null; - glyphPositions = null; + glyphTransforms[glyphIndex] = newTX; } } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkFontMetrics.java b/libjava/classpath/gnu/java/awt/peer/gtk/GdkFontMetrics.java deleted file mode 100644 index b2ffed10ea6..00000000000 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GdkFontMetrics.java +++ /dev/null @@ -1,143 +0,0 @@ -/* GdkFontMetrics.java - Copyright (C) 1999, 2002, 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.ClasspathToolkit; - -import java.awt.Font; -import java.awt.FontMetrics; -import java.awt.Toolkit; - -public class GdkFontMetrics extends FontMetrics -{ - - private int[] font_metrics; - GdkFontPeer peer; - - static final int FONT_METRICS_ASCENT = 0; - static final int FONT_METRICS_MAX_ASCENT = 1; - static final int FONT_METRICS_DESCENT = 2; - static final int FONT_METRICS_MAX_DESCENT = 3; - static final int FONT_METRICS_MAX_ADVANCE = 4; - - static final int TEXT_METRICS_X_BEARING = 0; - static final int TEXT_METRICS_Y_BEARING = 1; - static final int TEXT_METRICS_WIDTH = 2; - static final int TEXT_METRICS_HEIGHT = 3; - static final int TEXT_METRICS_X_ADVANCE = 4; - static final int TEXT_METRICS_Y_ADVANCE = 5; - - /** - * Makes sure to return a Font based on the given Font that has as - * peer a GdkFontPeer. Used in the initializer. - */ - private static Font initFont(Font font) - { - if (font == null) - return new Font("Dialog", Font.PLAIN, 12); - else if (font.getPeer() instanceof GdkFontPeer) - return font; - else - { - ClasspathToolkit toolkit; - toolkit = (ClasspathToolkit) Toolkit.getDefaultToolkit(); - return toolkit.getFont(font.getName(), font.getAttributes()); - } - } - - public GdkFontMetrics (Font font) - { - super(initFont(font)); - peer = (GdkFontPeer) this.font.getPeer(); - - font_metrics = new int[5]; - double [] hires = new double[5]; - peer.getFontMetrics (hires); - for (int i = 0; i < 5; ++i) - font_metrics[i] = (int) hires[i]; - } - - public int stringWidth (String str) - { - double [] hires = new double[6]; - peer.getTextMetrics(str, hires); - return (int) hires [TEXT_METRICS_WIDTH]; - } - - public int charWidth (char ch) - { - return stringWidth (new String (new char[] { ch })); - } - - public int charsWidth (char data[], int off, int len) - { - return stringWidth (new String (data, off, len)); - } - - public int getLeading () - { - // Sun always returns 0. - return 0; - } - - public int getAscent () - { - return font_metrics[FONT_METRICS_ASCENT]; - } - - public int getMaxAscent () - { - return font_metrics[FONT_METRICS_MAX_ASCENT]; - } - - public int getDescent () - { - return font_metrics[FONT_METRICS_DESCENT]; - } - - public int getMaxDescent () - { - return font_metrics[FONT_METRICS_MAX_DESCENT]; - } - - public int getMaxAdvance () - { - return font_metrics[FONT_METRICS_MAX_ADVANCE]; - } -} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkFontPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GdkFontPeer.java index 11635c3544e..5f5126ac590 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GdkFontPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GdkFontPeer.java @@ -38,6 +38,7 @@ exception statement from your version. */ package gnu.java.awt.peer.gtk; +import gnu.java.awt.ClasspathToolkit; import gnu.java.awt.peer.ClasspathFontPeer; import gnu.java.awt.font.opentype.NameDecoder; @@ -48,39 +49,125 @@ import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; import java.awt.font.GlyphMetrics; import java.awt.font.LineMetrics; +import java.awt.font.TextLayout; import java.awt.geom.Rectangle2D; import java.text.CharacterIterator; import java.util.Locale; import java.util.Map; -import java.util.ResourceBundle; import java.nio.ByteBuffer; import java.util.HashMap; public class GdkFontPeer extends ClasspathFontPeer { + static final FontRenderContext DEFAULT_CTX = + new FontRenderContext(null, false, false); + + /** + * Caches TextLayout instances for use in charsWidth() and drawString(). + * The size of the cache has been chosen so that relativly large GUIs with + * text documents are still efficient. + */ + HashMap textLayoutCache = new GtkToolkit.LRUCache(500); + + private class GdkFontMetrics extends FontMetrics + { + + public GdkFontMetrics (Font font) + { + super(initFont(font)); + } + + public int stringWidth (String str) + { + TextLayout tl = (TextLayout) textLayoutCache.get(str); + if (tl == null) + { + tl = new TextLayout(str, font, DEFAULT_CTX); + textLayoutCache.put(str, tl); + } + return (int) tl.getAdvance(); + } + + public int charWidth (char ch) + { + return stringWidth (new String (new char[] { ch })); + } + + public int charsWidth (char data[], int off, int len) + { + return stringWidth (new String (data, off, len)); + } + + public int getHeight() + { + return (int) height; + } + + public int getLeading () + { + return (int) (height - (ascent + descent)); + } + + public int getAscent () + { + return (int) ascent; + } + + public int getMaxAscent () + { + return (int) ascent; + } + + public int getDescent () + { + return (int) descent; + } + + public int getMaxDescent () + { + return (int) maxDescent; + } + + public int getMaxAdvance () + { + return (int) maxAdvance; + } + } + static native void initStaticState(); private final int native_state = GtkGenericPeer.getUniqueInteger (); - private static ResourceBundle bundle; /** * Cache GlyphMetrics objects. */ private HashMap metricsCache; - + + private static final int FONT_METRICS_ASCENT = 0; + private static final int FONT_METRICS_MAX_ASCENT = 1; + private static final int FONT_METRICS_DESCENT = 2; + private static final int FONT_METRICS_MAX_DESCENT = 3; + private static final int FONT_METRICS_MAX_ADVANCE = 4; + private static final int FONT_METRICS_HEIGHT = 5; + private static final int FONT_METRICS_UNDERLINE_OFFSET = 6; + private static final int FONT_METRICS_UNDERLINE_THICKNESS = 7; + + float ascent; + float descent; + float maxAscent; + float maxDescent; + float maxAdvance; + float height; + float underlineOffset; + float underlineThickness; + + GdkFontMetrics metrics; + static { System.loadLibrary("gtkpeer"); initStaticState (); - try - { - bundle = ResourceBundle.getBundle ("gnu.java.awt.peer.gtk.font"); - } - catch (Throwable ignored) - { - bundle = null; - } } private ByteBuffer nameTable = null; @@ -89,8 +176,8 @@ public class GdkFontPeer extends ClasspathFontPeer private native void dispose (); private native void setFont (String family, int style, int size); - native void getFontMetrics(double [] metrics); - native void getTextMetrics(String str, double [] metrics); + native synchronized void getFontMetrics(double [] metrics); + native synchronized void getTextMetrics(String str, double [] metrics); native void releasePeerGraphicsResource(); @@ -149,6 +236,7 @@ public class GdkFontPeer extends ClasspathFontPeer initState (); setFont (this.familyName, this.style, (int)this.size); metricsCache = new HashMap(); + setupMetrics(); } public GdkFontPeer (String name, Map attributes) @@ -157,6 +245,40 @@ public class GdkFontPeer extends ClasspathFontPeer initState (); setFont (this.familyName, this.style, (int)this.size); metricsCache = new HashMap(); + setupMetrics(); + } + + + /** + * Makes sure to return a Font based on the given Font that has as + * peer a GdkFontPeer. Used in the initializer. + */ + static Font initFont(Font font) + { + if (font == null) + return new Font("Dialog", Font.PLAIN, 12); + else if (font.getPeer() instanceof GdkFontPeer) + return font; + else + { + ClasspathToolkit toolkit; + toolkit = (ClasspathToolkit) Toolkit.getDefaultToolkit(); + return toolkit.getFont(font.getName(), font.getAttributes()); + } + } + + private void setupMetrics() + { + double [] hires = new double[8]; + getFontMetrics(hires); + ascent = (float) hires[FONT_METRICS_ASCENT]; + maxAscent = (float) hires[FONT_METRICS_MAX_ASCENT]; + descent = (float) hires[FONT_METRICS_DESCENT]; + maxDescent = (float) hires[FONT_METRICS_MAX_DESCENT]; + maxAdvance = (float) hires[FONT_METRICS_MAX_ADVANCE]; + height = (float) hires[FONT_METRICS_HEIGHT]; + underlineOffset = (float) hires[FONT_METRICS_UNDERLINE_OFFSET]; + underlineThickness = (float) hires[FONT_METRICS_UNDERLINE_THICKNESS]; } /** @@ -261,26 +383,17 @@ public class GdkFontPeer extends ClasspathFontPeer return Font.ROMAN_BASELINE; } - private static class GdkFontLineMetrics extends LineMetrics + private class GdkFontLineMetrics extends LineMetrics { - private FontMetrics fm; - private int nchars; - private float strikethroughOffset, strikethroughThickness, - underlineOffset, underlineThickness; - - public GdkFontLineMetrics (GdkFontPeer fp, FontMetrics m, int n) + private int nchars; + public GdkFontLineMetrics (GdkFontPeer fp, int n) { - fm = m; nchars = n; - strikethroughOffset = 0f; - underlineOffset = 0f; - strikethroughThickness = ((float)fp.getSize(null)) / 12f; - underlineThickness = strikethroughThickness; } public float getAscent() { - return (float) fm.getAscent (); + return ascent; } public int getBaselineIndex() @@ -296,27 +409,52 @@ public class GdkFontPeer extends ClasspathFontPeer public float getDescent() { - return (float) fm.getDescent (); + return descent; } public float getHeight() { - return (float) fm.getHeight (); + return height; } - public float getLeading() { return 0.f; } - public int getNumChars() { return nchars; } - public float getStrikethroughOffset() { return 0.f; } - public float getStrikethroughThickness() { return 0.f; } - public float getUnderlineOffset() { return 0.f; } - public float getUnderlineThickness() { return 0.f; } + public float getLeading() + { + return height - (ascent + descent); + } + + public int getNumChars() + { + return nchars; + } + + public float getStrikethroughOffset() + { + // FreeType doesn't seem to provide a value here. + return ascent / 2; + } + + public float getStrikethroughThickness() + { + // FreeType doesn't seem to provide a value here. + return 1.f; + } + + public float getUnderlineOffset() + { + return underlineOffset; + } + + public float getUnderlineThickness() + { + return underlineThickness; + } } public LineMetrics getLineMetrics (Font font, CharacterIterator ci, int begin, int limit, FontRenderContext rc) { - return new GdkFontLineMetrics (this, getFontMetrics (font), limit - begin); + return new GdkFontLineMetrics (this, limit - begin); } public Rectangle2D getMaxCharBounds (Font font, FontRenderContext rc) @@ -345,15 +483,6 @@ public class GdkFontPeer extends ClasspathFontPeer return buf.getShort(4); } - public Rectangle2D getStringBounds (Font font, CharacterIterator ci, - int begin, int limit, FontRenderContext frc) - { - GlyphVector gv = new FreetypeGlyphVector( font, - buildString(ci, begin, limit), - frc); - return gv.getVisualBounds(); - } - public boolean hasUniformLineMetrics (Font font) { return true; @@ -363,22 +492,21 @@ public class GdkFontPeer extends ClasspathFontPeer char[] chars, int start, int limit, int flags) { - return new FreetypeGlyphVector( font, new String( chars, start, - limit - start), + return new FreetypeGlyphVector( font, chars, start, limit - start, frc, flags); } public LineMetrics getLineMetrics (Font font, String str, FontRenderContext frc) { - return new GdkFontLineMetrics (this, getFontMetrics (font), str.length ()); + return new GdkFontLineMetrics (this, str.length ()); } public FontMetrics getFontMetrics (Font font) { - // Get the font metrics through GtkToolkit to take advantage of - // the metrics cache. - return Toolkit.getDefaultToolkit().getFontMetrics (font); + if (metrics == null) + metrics = new GdkFontMetrics(font); + return metrics; } /** @@ -397,4 +525,5 @@ public class GdkFontPeer extends ClasspathFontPeer { metricsCache.put( new Integer( glyphCode ), metrics ); } + } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphicsEnvironment.java b/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphicsEnvironment.java index e095c7dad4b..db725b697df 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphicsEnvironment.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphicsEnvironment.java @@ -44,7 +44,7 @@ import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.HeadlessException; import java.awt.image.BufferedImage; -import java.awt.image.DataBuffer; +import java.awt.image.Raster; import java.util.Locale; public class GdkGraphicsEnvironment extends GraphicsEnvironment @@ -103,9 +103,9 @@ public class GdkGraphicsEnvironment extends GraphicsEnvironment public Graphics2D createGraphics (BufferedImage image) { - DataBuffer db = image.getRaster().getDataBuffer(); - if(db instanceof CairoSurface) - return ((CairoSurface)db).getGraphics(); + Raster raster = image.getRaster(); + if(raster instanceof CairoSurface) + return ((CairoSurface)raster).getGraphics(); return new BufferedImageGraphics( image ); } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkChoicePeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkChoicePeer.java index f00461f3fc8..d866cefd33c 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkChoicePeer.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkChoicePeer.java @@ -84,7 +84,7 @@ public class GtkChoicePeer extends GtkComponentPeer public void select (int position) { - if (Thread.currentThread() == GtkToolkit.mainThread) + if (Thread.currentThread() == GtkMainThread.mainThread) selectNativeUnlocked (position); else selectNative (position); diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkComponentPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkComponentPeer.java index c11c45e2070..b1ef09d6e1b 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkComponentPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkComponentPeer.java @@ -90,6 +90,11 @@ public class GtkComponentPeer extends GtkGenericPeer Insets insets; + /** + * The current repaint area. Use should be guarded by synchronizing on this. + */ + private Rectangle currentPaintArea; + /* 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 */ @@ -308,13 +313,30 @@ public class GtkComponentPeer extends GtkGenericPeer // 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); + // Make sure that the paintArea includes the area from the event + // in the case when an application sends PaintEvents directly. + coalescePaintEvent(event); + Rectangle paintArea; + synchronized (this) + { + paintArea = currentPaintArea; + currentPaintArea = null; + } - g.dispose(); + if (paintArea != null) + { + Graphics g = getGraphics(); + try + { + g.setClip(paintArea); + awtComponent.paint(g); + } + finally + { + g.dispose(); + } + } } // This method and its overrides are the only methods in the peers @@ -327,13 +349,29 @@ public class GtkComponentPeer extends GtkGenericPeer || (awtComponent.getWidth() < 1 || awtComponent.getHeight() < 1)) return; - Graphics g = getGraphics(); - - g.setClip(event.getUpdateRect()); - - awtComponent.update(g); + // Make sure that the paintArea includes the area from the event + // in the case when an application sends PaintEvents directly. + coalescePaintEvent(event); + Rectangle paintArea; + synchronized (this) + { + paintArea = currentPaintArea; + currentPaintArea = null; + } - g.dispose(); + if (paintArea != null) + { + Graphics g = getGraphics(); + try + { + g.setClip(paintArea); + awtComponent.update(g); + } + finally + { + g.dispose(); + } + } } public boolean isFocusTraversable () @@ -514,7 +552,7 @@ public class GtkComponentPeer extends GtkGenericPeer y = 0; } - if (Thread.currentThread() == GtkToolkit.mainThread) + if (Thread.currentThread() == GtkMainThread.mainThread) gtkWidgetSetCursorUnlocked(cursor.getType(), image, x, y); else gtkWidgetSetCursor(cursor.getType(), image, x, y); @@ -562,7 +600,7 @@ public class GtkComponentPeer extends GtkGenericPeer b = (bounds.width > 0) && (bounds.height > 0); } - if (Thread.currentThread() == GtkToolkit.mainThread) + if (Thread.currentThread() == GtkMainThread.mainThread) setVisibleNativeUnlocked (b); else setVisibleNative (b); @@ -754,7 +792,14 @@ public class GtkComponentPeer extends GtkGenericPeer public void coalescePaintEvent (PaintEvent e) { - + synchronized (this) + { + Rectangle newRect = e.getUpdateRect(); + if (currentPaintArea == null) + currentPaintArea = newRect; + else + Rectangle.union(currentPaintArea, newRect, currentPaintArea); + } } public void updateCursorImmediately () diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkFramePeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkFramePeer.java index bb6f8b3bb3b..d113e92f5b4 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkFramePeer.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkFramePeer.java @@ -57,6 +57,11 @@ public class GtkFramePeer extends GtkWindowPeer native void removeMenuBarPeer (); native void gtkFixedSetVisible (boolean visible); + private native void maximize(); + private native void unmaximize(); + private native void iconify(); + private native void deiconify(); + int getMenuBarHeight () { return menuBar == null ? 0 : getMenuBarHeight (menuBar); @@ -199,12 +204,25 @@ public class GtkFramePeer extends GtkWindowPeer public int getState () { - return 0; + return windowState; } public void setState (int state) { - + switch (state) + { + case Frame.NORMAL: + if ((windowState & Frame.ICONIFIED) != 0) + deiconify(); + if ((windowState & Frame.MAXIMIZED_BOTH) != 0) + unmaximize(); + break; + case Frame.ICONIFIED: + iconify(); + break; + case Frame.MAXIMIZED_BOTH: + maximize(); + } } public void setMaximizedBounds (Rectangle r) diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkImageConsumer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkImageConsumer.java index f1a74b8cc99..53e97bb1a8a 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkImageConsumer.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkImageConsumer.java @@ -42,6 +42,7 @@ import java.awt.image.ColorModel; import java.awt.image.ImageConsumer; import java.awt.image.ImageProducer; import java.awt.image.MemoryImageSource; +import java.nio.ByteOrder; import java.util.Hashtable; /** @@ -103,7 +104,7 @@ public class GtkImageConsumer implements ImageConsumer scansize); } - public synchronized void setPixels (int x, int y, int width, int height, + public synchronized void setPixels (int x, int y, int width, int height, ColorModel cm, int[] pixels, int offset, int scansize) { @@ -117,18 +118,34 @@ public class GtkImageConsumer implements ImageConsumer width); else { - for (int i = 0; i < height; i++) - for (int j = 0; j < width; j++) - { - // get in AARRGGBB and convert to AABBGGRR - int pix = cm.getRGB(pixels[offset + (i * scansize) + x + j]); - byte b = (byte)(pix & 0xFF); - byte r = (byte)(((pix & 0x00FF0000) >> 16) & 0xFF); - pix &= 0xFF00FF00; - pix |= ((b & 0xFF) << 16); - pix |= (r & 0xFF); - pixelCache[(y + i) * this.width + x + j] = pix; - } + if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) + { + for (int i = 0; i < height; i++) + for (int j = 0; j < width; j++) + { + // get in RRGGBBAA and convert to AARRGGBB + int pix = cm.getRGB(pixels[offset + (i * scansize) + x + j]); + int a = ((pix & 0xFF000000) >> 24) & 0xFF; + int rgb = (pix & 0x00FFFFFF) << 8; + pix = rgb | a; + pixelCache[(y + i) * this.width + x + j] = pix; + } + } + else + { + for (int i = 0; i < height; i++) + for (int j = 0; j < width; j++) + { + // get in AARRGGBB and convert to AABBGGRR + int pix = cm.getRGB(pixels[offset + (i * scansize) + x + j]); + byte b = (byte)(pix & 0xFF); + byte r = (byte)(((pix & 0x00FF0000) >> 16) & 0xFF); + pix &= 0xFF00FF00; + pix |= ((b & 0xFF) << 16); + pix |= (r & 0xFF); + pixelCache[(y + i) * this.width + x + j] = pix; + } + } } } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkMainThread.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkMainThread.java new file mode 100644 index 00000000000..a4e280fe45f --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkMainThread.java @@ -0,0 +1,190 @@ +/* GtkMainThread.java -- Wrapper for the GTK main thread, and some utilities. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.peer.gtk; + +import gnu.java.awt.peer.NativeEventLoopRunningEvent; + +import java.awt.AWTEvent; + +/** + * The Java thread representing the native GTK main loop, that is, + * GtkMainThread.mainThread, terminates when GtkToolkit.gtkMain() + * returns. That happens in response to the last window peer being + * disposed (see GtkWindowPeer.dispose). + * + * When GtkMainThread.destroyWindow is called for the last window, it + * in turn calls GtkMainThread.endMainThread, which calls gtk_quit. + * gtk_quit signals gtk_main to return, which causes GtkMainThread.run + * to return. + * + * There should only be one native GTK main loop running at any given + * time. In order to safely start and stop the GTK main loop, we use + * a running flag and corresponding runningLock. startMainThread will + * not return until the native GTK main loop has started, as confirmed + * by the native set_running_flag callback setting the running flag to + * true. Without this protection, gtk_quit could be called before the + * main loop has actually started, which causes GTK assertion + * failures. Likewise endMainThread will not return until the native + * GTK main loop has ended. + * + * post_running_flag_callback is called during gtk_main initialization + * and no window can be created before startMainThread returns. This + * ensures that calling post_running_flag_callback is the first action + * taken by the native GTK main loop. + * + * GtkMainThread.mainThread is started when the window count goes from + * zero to one. + * + * GtkMainThread keeps the AWT event queue informed of its status by + * posting NativeEventLoopRunningEvents. The AWT event queue uses + * this status to determine whether or not the AWT exit conditions + * have been met (see EventQueue.isShutdown). + */ +public class GtkMainThread extends Thread +{ + /** Count of the number of open windows */ + private static int numberOfWindows = 0; + + /** Lock for the above */ + private static Object nWindowsLock = new Object(); + + /** Indicates whether or not the GTK main loop is running. */ + private static boolean running = false; + + /** Lock for the above. */ + private static Object runningLock = new Object(); + + /** The main thread instance (singleton) */ + public static GtkMainThread mainThread; + + /** Constructs a main thread */ + private GtkMainThread() + { + super("GTK main thread"); + } + + public void run () + { + GtkToolkit.gtkMain (); + } + + private static void setRunning(boolean running) + { + synchronized (runningLock) + { + GtkMainThread.running = running; + runningLock.notifyAll(); + } + } + + private static void startMainThread() + { + synchronized (runningLock) + { + if (!running) + { + mainThread = new GtkMainThread(); + mainThread.start(); + + while (!running) + { + try + { + runningLock.wait(); + } + catch (InterruptedException e) + { + System.err.println ("GtkMainThread.startMainThread:" + + " interrupted while waiting " + + " for GTK main loop to start"); + } + } + GtkGenericPeer.q() + .postEvent(new NativeEventLoopRunningEvent(new Boolean(true))); + } + } + } + + private static void endMainThread() + { + synchronized (runningLock) + { + if (running) + { + GtkToolkit.gtkQuit(); + + while (running) + { + try + { + runningLock.wait(); + } + catch (InterruptedException e) + { + System.err.println ("GtkMainThread.endMainThread:" + + " interrupted while waiting " + + " for GTK main loop to stop"); + } + } + GtkGenericPeer.q() + .postEvent(new NativeEventLoopRunningEvent(new Boolean(false))); + } + } + } + + public static void createWindow() + { + synchronized (nWindowsLock) + { + if (numberOfWindows == 0) + startMainThread(); + numberOfWindows++; + } + } + + public static void destroyWindow() + { + synchronized (nWindowsLock) + { + numberOfWindows--; + if (numberOfWindows == 0) + endMainThread(); + } + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkToolkit.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkToolkit.java index 6aa87fc2ecf..f746a47479f 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkToolkit.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkToolkit.java @@ -62,6 +62,7 @@ import java.awt.FontMetrics; import java.awt.Frame; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; +import java.awt.HeadlessException; import java.awt.Image; import java.awt.Label; import java.awt.List; @@ -83,6 +84,7 @@ import java.awt.dnd.DragGestureEvent; import java.awt.dnd.DragGestureListener; import java.awt.dnd.DragGestureRecognizer; import java.awt.dnd.DragSource; +import java.awt.dnd.InvalidDnDOperationException; import java.awt.dnd.peer.DragSourceContextPeer; import java.awt.im.InputMethodHighlight; import java.awt.image.ColorModel; @@ -115,7 +117,6 @@ import java.awt.peer.WindowPeer; import java.io.InputStream; import java.net.URL; import java.util.HashMap; -import java.util.Hashtable; import java.util.LinkedHashMap; import java.util.Map; import java.util.Properties; @@ -131,37 +132,30 @@ import javax.imageio.spi.IIORegistry; public class GtkToolkit extends gnu.java.awt.ClasspathToolkit { - Hashtable containers = new Hashtable(); - static EventQueue q; - static Thread mainThread; + private static EventQueue q; static native void gtkInit(int portableNativeSync); + static native void gtkMain(); + + static native void gtkQuit(); + static { System.loadLibrary("gtkpeer"); - + int portableNativeSync; String portNatSyncProp = System.getProperty("gnu.classpath.awt.gtk.portable.native.sync"); - + if (portNatSyncProp == null) portableNativeSync = -1; // unset else if (Boolean.valueOf(portNatSyncProp).booleanValue()) portableNativeSync = 1; // true else portableNativeSync = 0; // false - + gtkInit(portableNativeSync); - - mainThread = new Thread ("GTK main thread") - { - public void run () - { - gtkMain (); - } - }; - mainThread.start (); } public GtkToolkit () @@ -169,6 +163,7 @@ public class GtkToolkit extends gnu.java.awt.ClasspathToolkit } public native void beep(); + private native void getScreenSizeDimensions(int[] xy); public int checkImage (Image image, int width, int height, @@ -181,6 +176,9 @@ public class GtkToolkit extends gnu.java.awt.ClasspathToolkit if (image instanceof GtkImage) return ((GtkImage) image).checkImage (observer); + if (image instanceof AsyncImage) + return ((AsyncImage) image).checkImage(observer); + if (observer != null) observer.imageUpdate (image, status, -1, -1, @@ -194,7 +192,7 @@ public class GtkToolkit extends gnu.java.awt.ClasspathToolkit * Helper to return either a Image -- the argument -- or a * GtkImage with the errorLoading flag set if the argument is null. */ - private Image imageOrError(Image b) + static Image imageOrError(Image b) { if (b == null) return GtkImage.getErrorImage(); @@ -221,16 +219,7 @@ public class GtkToolkit extends gnu.java.awt.ClasspathToolkit public Image createImage (URL url) { - Image image; - try - { - image = CairoSurface.getBufferedImage( new GtkImage( url ) ); - } - catch (IllegalArgumentException iae) - { - image = null; - } - return imageOrError(image); + return new AsyncImage(url); } public Image createImage (ImageProducer producer) @@ -301,7 +290,7 @@ public class GtkToolkit extends gnu.java.awt.ClasspathToolkit "SansSerif" }); } - private class LRUCache extends LinkedHashMap + static class LRUCache extends LinkedHashMap { int max_entries; public LRUCache(int max) @@ -316,23 +305,11 @@ public class GtkToolkit extends gnu.java.awt.ClasspathToolkit } private LRUCache fontCache = new LRUCache(50); - private LRUCache metricsCache = new LRUCache(50); private LRUCache imageCache = new LRUCache(50); public FontMetrics getFontMetrics (Font font) { - synchronized (metricsCache) - { - if (metricsCache.containsKey(font)) - return (FontMetrics) metricsCache.get(font); - } - - FontMetrics m = new GdkFontMetrics (font); - synchronized (metricsCache) - { - metricsCache.put(font, m); - } - return m; + return ((GdkFontPeer) font.getPeer()).getFontMetrics(font); } public Image getImage (String filename) @@ -408,6 +385,13 @@ public class GtkToolkit extends gnu.java.awt.ClasspathToolkit return ((((GtkImage)image).checkImage (observer) & ImageObserver.ALLBITS) != 0); + if (image instanceof AsyncImage) + { + AsyncImage aImg = (AsyncImage) image; + aImg.addObserver(observer); + return aImg.realImage != null; + } + /* Assume anything else is too */ return true; } @@ -437,106 +421,131 @@ public class GtkToolkit extends gnu.java.awt.ClasspathToolkit protected ButtonPeer createButton (Button b) { + checkHeadless(); return new GtkButtonPeer (b); } protected CanvasPeer createCanvas (Canvas c) { + checkHeadless(); return new GtkCanvasPeer (c); } protected CheckboxPeer createCheckbox (Checkbox cb) { + checkHeadless(); return new GtkCheckboxPeer (cb); } protected CheckboxMenuItemPeer createCheckboxMenuItem (CheckboxMenuItem cmi) { + checkHeadless(); return new GtkCheckboxMenuItemPeer (cmi); } protected ChoicePeer createChoice (Choice c) { + checkHeadless(); return new GtkChoicePeer (c); } protected DialogPeer createDialog (Dialog d) { + checkHeadless(); + GtkMainThread.createWindow(); return new GtkDialogPeer (d); } protected FileDialogPeer createFileDialog (FileDialog fd) { + checkHeadless(); return new GtkFileDialogPeer (fd); } protected FramePeer createFrame (Frame f) { + checkHeadless(); + GtkMainThread.createWindow(); return new GtkFramePeer (f); } protected LabelPeer createLabel (Label label) { + checkHeadless(); return new GtkLabelPeer (label); } protected ListPeer createList (List list) { + checkHeadless(); return new GtkListPeer (list); } protected MenuPeer createMenu (Menu m) { + checkHeadless(); return new GtkMenuPeer (m); } protected MenuBarPeer createMenuBar (MenuBar mb) { + checkHeadless(); return new GtkMenuBarPeer (mb); } protected MenuItemPeer createMenuItem (MenuItem mi) { + checkHeadless(); return new GtkMenuItemPeer (mi); } protected PanelPeer createPanel (Panel p) { + checkHeadless(); return new GtkPanelPeer (p); } protected PopupMenuPeer createPopupMenu (PopupMenu target) { + checkHeadless(); return new GtkPopupMenuPeer (target); } protected ScrollPanePeer createScrollPane (ScrollPane sp) { + checkHeadless(); return new GtkScrollPanePeer (sp); } protected ScrollbarPeer createScrollbar (Scrollbar sb) { + checkHeadless(); return new GtkScrollbarPeer (sb); } protected TextAreaPeer createTextArea (TextArea ta) { + checkHeadless(); return new GtkTextAreaPeer (ta); } protected TextFieldPeer createTextField (TextField tf) { + checkHeadless(); return new GtkTextFieldPeer (tf); } protected WindowPeer createWindow (Window w) { + checkHeadless(); + GtkMainThread.createWindow(); return new GtkWindowPeer (w); } public EmbeddedWindowPeer createEmbeddedWindow (EmbeddedWindow w) { + checkHeadless(); + GtkMainThread.createWindow(); return new GtkEmbeddedWindowPeer (w); } @@ -605,6 +614,8 @@ public class GtkToolkit extends gnu.java.awt.ClasspathToolkit public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent e) { + if (GraphicsEnvironment.isHeadless()) + throw new InvalidDnDOperationException(); return new GtkDragSourceContextPeer(e); } @@ -614,7 +625,8 @@ public class GtkToolkit extends gnu.java.awt.ClasspathToolkit int actions, DragGestureListener l) { - if (recognizer.getName().equals("java.awt.dnd.MouseDragGestureRecognizer")) + if (recognizer.getName().equals("java.awt.dnd.MouseDragGestureRecognizer") + && ! GraphicsEnvironment.isHeadless()) { GtkMouseDragGestureRecognizer gestureRecognizer = new GtkMouseDragGestureRecognizer(ds, comp, actions, l); @@ -661,13 +673,25 @@ public class GtkToolkit extends gnu.java.awt.ClasspathToolkit GdkPixbufDecoder.registerSpis(reg); } - public static native void gtkMain(); - protected MouseInfoPeer getMouseInfoPeer() { return new GtkMouseInfoPeer(); } + public boolean isFrameStateSupported(int state) + { + // GTK supports ICONFIED, NORMAL and MAXIMIZE_BOTH, but + // not (yet?) MAXIMIZE_VERT and MAXIMIZE_HORIZ. + return state == Frame.NORMAL || state == Frame.ICONIFIED + || state == Frame.MAXIMIZED_BOTH; + } + + private void checkHeadless() + { + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException(); + } + public native int getMouseNumberOfButtons(); } // class GtkToolkit diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkVolatileImage.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkVolatileImage.java index 44e7b027b0b..8660ced8ec1 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkVolatileImage.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkVolatileImage.java @@ -37,13 +37,21 @@ exception statement from your version. */ package gnu.java.awt.peer.gtk; -import java.awt.ImageCapabilities; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; +import java.awt.ImageCapabilities; +import java.awt.Point; import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.DirectColorModel; import java.awt.image.ImageObserver; +import java.awt.image.Raster; +import java.awt.image.SampleModel; +import java.awt.image.SinglePixelPackedSampleModel; import java.awt.image.VolatileImage; +import java.awt.image.WritableRaster; public class GtkVolatileImage extends VolatileImage { @@ -52,6 +60,12 @@ public class GtkVolatileImage extends VolatileImage final GtkComponentPeer component; + static ColorModel gdkColorModel = new DirectColorModel(32, + 0x000000FF, + 0x0000FF00, + 0x00FF0000, + 0xFF000000); + /** * Don't touch, accessed from native code. */ @@ -62,6 +76,17 @@ public class GtkVolatileImage extends VolatileImage native void destroy(long pointer); native int[] nativeGetPixels(long pointer); + + /** + * Gets the pixels in the current image from GDK. + * + * Note that pixels are in 32-bit RGBA, non-premultiplied, which is different + * from Cairo's premultiplied ARGB, which is different from Java's standard + * non-premultiplied ARGB. Caution is advised when using this method, to + * ensure that the data format remains consistent with what you expect. + * + * @return the current pixels, as reported by GDK. + */ public int[] getPixels() { return nativeGetPixels(nativePointer); @@ -113,9 +138,11 @@ public class GtkVolatileImage extends VolatileImage public BufferedImage getSnapshot() { - CairoSurface cs = new CairoSurface( width, height ); - cs.setPixels( getPixels() ); - return CairoSurface.getBufferedImage( cs ); + WritableRaster raster = Raster.createWritableRaster(createGdkSampleModel(width, height), + new Point(0, 0)); + raster.setDataElements(0, 0, getPixels()); + return new BufferedImage(gdkColorModel, raster, + gdkColorModel.isAlphaPremultiplied(), null); } public Graphics getGraphics() @@ -167,4 +194,14 @@ public class GtkVolatileImage extends VolatileImage { return null; } + + /** + * Creates a SampleModel that matches GDK's native format + */ + protected static SampleModel createGdkSampleModel(int w, int h) + { + return new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, w, h, + new int[]{0x000000FF, 0x0000FF00, + 0x00FF0000, 0xFF000000}); + } } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkWindowPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkWindowPeer.java index 866d9c8816a..8d49719b1d3 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkWindowPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkWindowPeer.java @@ -38,7 +38,10 @@ exception statement from your version. */ package gnu.java.awt.peer.gtk; +import gnu.java.awt.ComponentReshapeEvent; + import java.awt.Component; +import java.awt.Font; import java.awt.Frame; import java.awt.Graphics; import java.awt.KeyboardFocusManager; @@ -62,8 +65,7 @@ public class GtkWindowPeer extends GtkContainerPeer protected static final int GDK_WINDOW_TYPE_HINT_DOCK = 6; protected static final int GDK_WINDOW_TYPE_HINT_DESKTOP = 7; - private boolean hasBeenShown = false; - private int oldState = Frame.NORMAL; + protected int windowState = Frame.NORMAL; // Cached awt window component location, width and height. private int x, y, width, height; @@ -75,6 +77,12 @@ public class GtkWindowPeer extends GtkContainerPeer native boolean gtkWindowHasFocus(); native void realize (); + public void dispose() + { + super.dispose(); + GtkMainThread.destroyWindow(); + } + /** Returns the cached width of the AWT window component. */ int getX () { @@ -144,6 +152,8 @@ public class GtkWindowPeer extends GtkContainerPeer public GtkWindowPeer (Window window) { super (window); + // Set reasonable font for the window. + window.setFont(new Font("Dialog", Font.PLAIN, 12)); } public native void toBack(); @@ -218,9 +228,31 @@ public class GtkWindowPeer extends GtkContainerPeer // only called from GTK thread protected void postConfigureEvent (int x, int y, int width, int height) { + int frame_x = x - insets.left; + int frame_y = y - insets.top; int frame_width = width + insets.left + insets.right; int frame_height = height + insets.top + insets.bottom; + // Update the component's knowledge about the size. + // Important: Please look at the big comment in ComponentReshapeEvent + // to learn why we did it this way. If you change this code, make + // sure that the peer->AWT bounds update still works. + // (for instance: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29448 ) + + // We do this befor we post the ComponentEvent, because (in Window) + // we invalidate() / revalidate() when a ComponentEvent is seen, + // and the AWT must already know about the new size then. + if (frame_x != this.x || frame_y != this.y || frame_width != this.width + || frame_height != this.height) + { + ComponentReshapeEvent ev = new ComponentReshapeEvent(awtComponent, + frame_x, + frame_y, + frame_width, + frame_height); + awtComponent.dispatchEvent(ev); + } + if (frame_width != getWidth() || frame_height != getHeight()) { @@ -230,9 +262,6 @@ public class GtkWindowPeer extends GtkContainerPeer ComponentEvent.COMPONENT_RESIZED)); } - int frame_x = x - insets.left; - int frame_y = y - insets.top; - if (frame_x != getX() || frame_y != getY()) { @@ -241,6 +270,7 @@ public class GtkWindowPeer extends GtkContainerPeer q().postEvent(new ComponentEvent(awtComponent, ComponentEvent.COMPONENT_MOVED)); } + } public void show () @@ -255,23 +285,26 @@ public class GtkWindowPeer extends GtkContainerPeer void postWindowEvent (int id, Window opposite, int newState) { - if (id == WindowEvent.WINDOW_OPENED) + if (id == WindowEvent.WINDOW_STATE_CHANGED) { - // Post a WINDOW_OPENED event the first time this window is shown. - if (!hasBeenShown) + if (windowState != newState) { + // Post old styleWindowEvent with WINDOW_ICONIFIED or + // WINDOW_DEICONIFIED if appropriate. + if ((windowState & Frame.ICONIFIED) != 0 + && (newState & Frame.ICONIFIED) == 0) + q().postEvent(new WindowEvent((Window) awtComponent, + WindowEvent.WINDOW_DEICONIFIED, + opposite, 0, 0)); + else if ((windowState & Frame.ICONIFIED) == 0 + && (newState & Frame.ICONIFIED) != 0) + q().postEvent(new WindowEvent((Window) awtComponent, + WindowEvent.WINDOW_ICONIFIED, + opposite, 0, 0)); + // Post new-style WindowStateEvent. q().postEvent (new WindowEvent ((Window) awtComponent, id, - opposite)); - hasBeenShown = true; - } - } - else if (id == WindowEvent.WINDOW_STATE_CHANGED) - { - if (oldState != newState) - { - q().postEvent (new WindowEvent ((Window) awtComponent, id, opposite, - oldState, newState)); - oldState = newState; + opposite, windowState, newState)); + windowState = newState; } } else @@ -350,13 +383,6 @@ public class GtkWindowPeer extends GtkContainerPeer 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) { diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/VolatileImageGraphics.java b/libjava/classpath/gnu/java/awt/peer/gtk/VolatileImageGraphics.java index 58496559320..62dbb45d81a 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/VolatileImageGraphics.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/VolatileImageGraphics.java @@ -38,15 +38,32 @@ exception statement from your version. */ package gnu.java.awt.peer.gtk; +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Composite; import java.awt.Graphics; +import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.Image; +import java.awt.Point; +import java.awt.Shape; +import java.awt.Toolkit; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; +import java.util.Hashtable; public class VolatileImageGraphics extends ComponentGraphics { private GtkVolatileImage owner; + private BufferedImage buffer; public VolatileImageGraphics(GtkVolatileImage img) { @@ -77,10 +94,118 @@ public class VolatileImageGraphics extends ComponentGraphics return new VolatileImageGraphics( this ); } + public void draw(Shape s) + { + if (comp == null || comp instanceof AlphaComposite) + super.draw(s); + + // Custom composite + else + { + // Draw operation to temporary buffer + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setColor(this.getColor()); + g2d.setStroke(this.getStroke()); + g2d.draw(s); + + drawComposite(s.getBounds2D(), null); + } + } + + public void fill(Shape s) + { + if (comp == null || comp instanceof AlphaComposite) + super.fill(s); + + // Custom composite + else + { + // Draw operation to temporary buffer + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setPaint(this.getPaint()); + g2d.setColor(this.getColor()); + g2d.fill(s); + + drawComposite(s.getBounds2D(), null); + } + } + + public void drawGlyphVector(GlyphVector gv, float x, float y) + { + if (comp == null || comp instanceof AlphaComposite) + super.drawGlyphVector(gv, x, y); + + // Custom composite + else + { + // Draw operation to temporary buffer + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + + g2d.setPaint(this.getPaint()); + g2d.setColor(this.getColor()); + g2d.drawGlyphVector(gv, x, y); + + Rectangle2D bounds = gv.getLogicalBounds(); + bounds = new Rectangle2D.Double(x + bounds.getX(), y + bounds.getY(), + bounds.getWidth(), bounds.getHeight()); + drawComposite(bounds, null); + } + } + + protected boolean drawImage(Image img, AffineTransform xform, + Color bgcolor, ImageObserver obs) + { + if (comp == null || comp instanceof AlphaComposite) + return super.drawImage(img, xform, bgcolor, obs); + + // Custom composite + else + { + // Get buffered image of source + if( !(img instanceof BufferedImage) ) + { + ImageProducer source = img.getSource(); + if (source == null) + return false; + img = Toolkit.getDefaultToolkit().createImage(source); + } + BufferedImage bImg = (BufferedImage) img; + + // Find dimensions of translation + Point2D origin = new Point2D.Double(bImg.getMinX(), bImg.getMinY()); + Point2D pt = new Point2D.Double(bImg.getWidth(), bImg.getHeight()); + if (xform != null) + { + origin = xform.transform(origin, origin); + pt = xform.transform(pt, pt); + } + + // Create buffer and draw image + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setRenderingHints(this.getRenderingHints()); + g2d.drawImage(img, xform, obs); + // Perform compositing from buffer to screen + return drawComposite(new Rectangle2D.Double((int)origin.getX(), + (int)origin.getY(), + (int)pt.getX(), + (int)pt.getY()), + obs); + } + } + public boolean drawImage(Image img, int x, int y, ImageObserver observer) { - if( img instanceof GtkVolatileImage ) + if (img instanceof GtkVolatileImage + && (comp == null || comp instanceof AlphaComposite)) { owner.drawVolatile( ((GtkVolatileImage)img).nativePointer, x, y, @@ -94,7 +219,8 @@ public class VolatileImageGraphics extends ComponentGraphics public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { - if( img instanceof GtkVolatileImage ) + if ((img instanceof GtkVolatileImage) + && (comp == null || comp instanceof AlphaComposite)) { owner.drawVolatile( ((GtkVolatileImage)img).nativePointer, x, y, width, height ); @@ -107,5 +233,84 @@ public class VolatileImageGraphics extends ComponentGraphics { return new Rectangle2D.Double(0, 0, owner.width, owner.height); } + + private boolean drawComposite(Rectangle2D bounds, ImageObserver observer) + { + // Clip source to visible areas that need updating + Rectangle2D clip = this.getClipBounds(); + Rectangle2D.intersect(bounds, clip, bounds); + + BufferedImage buffer2 = buffer; + if (!bounds.equals(buffer2.getRaster().getBounds())) + buffer2 = buffer2.getSubimage((int)bounds.getX(), (int)bounds.getY(), + (int)bounds.getWidth(), + (int)bounds.getHeight()); + + // Get current on-screen pixels (destination) and clip to bounds + BufferedImage current = owner.getSnapshot(); + + double[] points = new double[] {bounds.getX(), bounds.getY(), + bounds.getMaxX(), bounds.getMaxY()}; + transform.transform(points, 0, points, 0, 2); + + Rectangle2D deviceBounds = new Rectangle2D.Double(points[0], points[1], + points[2] - points[0], + points[3] - points[1]); + Rectangle2D.intersect(deviceBounds, this.getClipInDevSpace(), deviceBounds); + + current = current.getSubimage((int)deviceBounds.getX(), + (int)deviceBounds.getY(), + (int)deviceBounds.getWidth(), + (int)deviceBounds.getHeight()); + + // Perform actual composite operation + compCtx.compose(buffer2.getRaster(), current.getRaster(), + buffer2.getRaster()); + + // This MUST call directly into the "action" method in CairoGraphics2D, + // not one of the wrappers, to ensure that the composite isn't processed + // more than once! + Composite oldComp = comp; // so that ComponentGraphics doesn't + comp = null; // process the composite again + boolean rv = super.drawImage(buffer2, + AffineTransform.getTranslateInstance(bounds.getX(), + bounds.getY()), + null, null); + comp = oldComp; + + return rv; + } + + private void createBuffer() + { + if (buffer == null) + { + WritableRaster rst; + rst = Raster.createWritableRaster(GtkVolatileImage.createGdkSampleModel(owner.width, + owner.height), + new Point(0,0)); + + buffer = new BufferedImage(GtkVolatileImage.gdkColorModel, rst, + GtkVolatileImage.gdkColorModel.isAlphaPremultiplied(), + new Hashtable()); + } + else + { + Graphics2D g2d = ((Graphics2D)buffer.getGraphics()); + + g2d.setBackground(new Color(0,0,0,0)); + g2d.clearRect(0, 0, buffer.getWidth(), buffer.getHeight()); + } + } + + protected ColorModel getNativeCM() + { + // We should really return GtkVolatileImage.gdkColorModel , + // but CairoGraphics2D doesn't handle alpha premultiplication properly (see + // the fixme in drawImage) so we use the naive Cairo model instead to trick + // the compositing context. + // Because getNativeCM() == getBufferCM() for this peer, it doesn't break. + return CairoSurface.cairoCM_pre; + } } diff --git a/libjava/classpath/gnu/java/awt/peer/headless/HeadlessGraphicsEnvironment.java b/libjava/classpath/gnu/java/awt/peer/headless/HeadlessGraphicsEnvironment.java new file mode 100644 index 00000000000..b3eeb1baa84 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/headless/HeadlessGraphicsEnvironment.java @@ -0,0 +1,118 @@ +/* HeadlessGraphicsEnvironment.java -- A graphics environment for headless mode + 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.headless; + +import gnu.java.awt.java2d.RasterGraphics; + +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.HeadlessException; +import java.awt.image.BufferedImage; +import java.awt.image.Raster; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.Locale; + +public class HeadlessGraphicsEnvironment + extends GraphicsEnvironment +{ + + public Graphics2D createGraphics(BufferedImage image) + { + Graphics2D g2d; + try + { + // Try to get a CairoGraphics (accellerated) when available. Do this + // via reflection to avoid having a hard compile time dependency. + Class cairoSurfaceCl = + Class.forName("gnu.java.awt.peer.gtk.CairoSurface"); + Raster raster = image.getRaster(); + if (cairoSurfaceCl.isInstance(raster)) + { + Method getGraphicsM = cairoSurfaceCl.getMethod("getGraphics", + new Class[0]); + g2d = (Graphics2D) getGraphicsM.invoke(raster, new Object[0]); + } + else + { + Class bigCl = + Class.forName("gnu.java.awt.peer.gtk.BufferedImageGraphics"); + Constructor bigC = + bigCl.getConstructor(new Class[]{BufferedImage.class }); + g2d = (Graphics2D) bigC.newInstance(new Object[]{ image}); + } + } + catch (Exception ex) + { + g2d = new RasterGraphics(image.getRaster(), image.getColorModel()); + } + return g2d; + } + + public Font[] getAllFonts() + { + // FIXME: Implement. + return null; + } + + public String[] getAvailableFontFamilyNames() + { + // FIXME: Implement. + return null; + } + + public String[] getAvailableFontFamilyNames(Locale l) + { + // FIXME: Implement. + return null; + } + + public GraphicsDevice getDefaultScreenDevice() + { + throw new HeadlessException(); + } + + public GraphicsDevice[] getScreenDevices() + { + throw new HeadlessException(); + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/headless/HeadlessToolkit.java b/libjava/classpath/gnu/java/awt/peer/headless/HeadlessToolkit.java new file mode 100644 index 00000000000..96798c9e9d8 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/headless/HeadlessToolkit.java @@ -0,0 +1,371 @@ +/* HeadlessToolkit.java -- A toolkit for headless mode + 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.headless; + +import gnu.java.awt.ClasspathToolkit; +import gnu.java.awt.EmbeddedWindow; +import gnu.java.awt.peer.ClasspathFontPeer; +import gnu.java.awt.peer.EmbeddedWindowPeer; + +import java.awt.AWTException; +import java.awt.Button; +import java.awt.Canvas; +import java.awt.Checkbox; +import java.awt.CheckboxMenuItem; +import java.awt.Choice; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FileDialog; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Frame; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.HeadlessException; +import java.awt.Image; +import java.awt.Label; +import java.awt.List; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.Panel; +import java.awt.PopupMenu; +import java.awt.PrintJob; +import java.awt.ScrollPane; +import java.awt.Scrollbar; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.Window; +import java.awt.datatransfer.Clipboard; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.peer.DragSourceContextPeer; +import java.awt.im.InputMethodHighlight; +import java.awt.image.ColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.peer.ButtonPeer; +import java.awt.peer.CanvasPeer; +import java.awt.peer.CheckboxMenuItemPeer; +import java.awt.peer.CheckboxPeer; +import java.awt.peer.ChoicePeer; +import java.awt.peer.DialogPeer; +import java.awt.peer.FileDialogPeer; +import java.awt.peer.FontPeer; +import java.awt.peer.FramePeer; +import java.awt.peer.LabelPeer; +import java.awt.peer.ListPeer; +import java.awt.peer.MenuBarPeer; +import java.awt.peer.MenuItemPeer; +import java.awt.peer.MenuPeer; +import java.awt.peer.PanelPeer; +import java.awt.peer.PopupMenuPeer; +import java.awt.peer.RobotPeer; +import java.awt.peer.ScrollPanePeer; +import java.awt.peer.ScrollbarPeer; +import java.awt.peer.TextAreaPeer; +import java.awt.peer.TextFieldPeer; +import java.awt.peer.WindowPeer; +import java.io.InputStream; +import java.net.URL; +import java.util.Map; +import java.util.Properties; + +public class HeadlessToolkit + extends ClasspathToolkit +{ + + /** + * The graphics environment for headless graphics. + */ + private HeadlessGraphicsEnvironment graphicsEnv; + + public void beep() + { + // TODO Auto-generated method stub + + } + + public int checkImage(Image image, int width, int height, + ImageObserver observer) + { + // TODO Auto-generated method stub + return 0; + } + + protected ButtonPeer createButton(Button target) + { + throw new HeadlessException(); + } + + protected CanvasPeer createCanvas(Canvas target) + { + throw new HeadlessException(); + } + + protected CheckboxPeer createCheckbox(Checkbox target) + { + throw new HeadlessException(); + } + + protected CheckboxMenuItemPeer createCheckboxMenuItem(CheckboxMenuItem target) + { + throw new HeadlessException(); + } + + protected ChoicePeer createChoice(Choice target) + { + throw new HeadlessException(); + } + + protected DialogPeer createDialog(Dialog target) + { + throw new HeadlessException(); + } + + public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent e) + { + throw new HeadlessException(); + } + + protected FileDialogPeer createFileDialog(FileDialog target) + { + throw new HeadlessException(); + } + + protected FramePeer createFrame(Frame target) + { + throw new HeadlessException(); + } + + public Image createImage(String filename) + { + // FIXME: Implement. + return null; + } + + public Image createImage(URL url) + { + // FIXME: Implement. + return null; + } + + public Image createImage(ImageProducer producer) + { + // FIXME: Implement. + return null; + } + + public Image createImage(byte[] data, int offset, int len) + { + // TODO Auto-generated method stub + return null; + } + + protected LabelPeer createLabel(Label target) + { + throw new HeadlessException(); + } + + protected ListPeer createList(List target) + { + throw new HeadlessException(); + } + + protected MenuPeer createMenu(Menu target) + { + throw new HeadlessException(); + } + + protected MenuBarPeer createMenuBar(MenuBar target) + { + throw new HeadlessException(); + } + + protected MenuItemPeer createMenuItem(MenuItem target) + { + throw new HeadlessException(); + } + + protected PanelPeer createPanel(Panel target) + { + throw new HeadlessException(); + } + + protected PopupMenuPeer createPopupMenu(PopupMenu target) + { + throw new HeadlessException(); + } + + protected ScrollPanePeer createScrollPane(ScrollPane target) + { + throw new HeadlessException(); + } + + protected ScrollbarPeer createScrollbar(Scrollbar target) + { + throw new HeadlessException(); + } + + protected TextAreaPeer createTextArea(TextArea target) + { + throw new HeadlessException(); + } + + protected TextFieldPeer createTextField(TextField target) + { + throw new HeadlessException(); + } + + protected WindowPeer createWindow(Window target) + { + throw new HeadlessException(); + } + + public ColorModel getColorModel() + { + // TODO Auto-generated method stub + return null; + } + + public String[] getFontList() + { + // TODO Auto-generated method stub + return null; + } + + public FontMetrics getFontMetrics(Font name) + { + // TODO Auto-generated method stub + return null; + } + + protected FontPeer getFontPeer(String name, int style) + { + // TODO Auto-generated method stub + return null; + } + + public Image getImage(String name) + { + // TODO Auto-generated method stub + return null; + } + + public Image getImage(URL url) + { + // TODO Auto-generated method stub + return null; + } + + public PrintJob getPrintJob(Frame frame, String title, Properties props) + { + // TODO Auto-generated method stub + return null; + } + + public int getScreenResolution() + { + throw new HeadlessException(); + } + + public Dimension getScreenSize() + { + throw new HeadlessException(); + } + + public Clipboard getSystemClipboard() + { + throw new HeadlessException(); + } + + protected EventQueue getSystemEventQueueImpl() + { + throw new HeadlessException(); + } + + public Map mapInputMethodHighlight(InputMethodHighlight highlight) + { + // TODO Auto-generated method stub + return null; + } + + public boolean prepareImage(Image image, int width, int height, + ImageObserver observer) + { + // TODO Auto-generated method stub + return false; + } + + public void sync() + { + // TODO Auto-generated method stub + + } + + public EmbeddedWindowPeer createEmbeddedWindow(EmbeddedWindow w) + { + throw new HeadlessException(); + } + + public Font createFont(int format, InputStream stream) + { + // TODO Auto-generated method stub + return null; + } + + public RobotPeer createRobot(GraphicsDevice screen) throws AWTException + { + throw new HeadlessException(); + } + + public ClasspathFontPeer getClasspathFontPeer(String name, Map attrs) + { + // TODO Auto-generated method stub + return null; + } + + public GraphicsEnvironment getLocalGraphicsEnvironment() + { + if (graphicsEnv == null) + graphicsEnv = new HeadlessGraphicsEnvironment(); + return graphicsEnv; + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtFontPeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtFontPeer.java index d847a805396..6ffe3f69176 100644 --- a/libjava/classpath/gnu/java/awt/peer/qt/QtFontPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtFontPeer.java @@ -194,15 +194,4 @@ public class QtFontPeer extends ClasspathFontPeer throw new UnsupportedOperationException(); } - public Rectangle2D getStringBounds (Font font, - CharacterIterator ci, - int begin, int limit, - FontRenderContext frc) - { - int index = begin; - String s = "" + ci.setIndex( index ); - while( index++ <= limit ) - s = s + ci.next(); - return metrics.getStringBounds(s); - } } diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtGraphics.java b/libjava/classpath/gnu/java/awt/peer/qt/QtGraphics.java index 842cbbbf8ba..5694e8d2076 100644 --- a/libjava/classpath/gnu/java/awt/peer/qt/QtGraphics.java +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtGraphics.java @@ -669,12 +669,13 @@ public abstract class QtGraphics extends Graphics2D public RenderingHints getRenderingHints() { - return new RenderingHints( renderingHints ); + return (RenderingHints) renderingHints.clone(); } - public void setRenderingHints(Map hints) + public void setRenderingHints(Map<?,?> hints) { - renderingHints = new RenderingHints( hints ); + renderingHints = new RenderingHints( null ); + renderingHints.putAll(hints); updateRenderingHints(); } diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingButtonPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingButtonPeer.java index 2357fcbfb0d..531d6f2db3b 100644 --- a/libjava/classpath/gnu/java/awt/peer/swing/SwingButtonPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingButtonPeer.java @@ -38,6 +38,7 @@ exception statement from your version. */ package gnu.java.awt.peer.swing; import java.awt.Button; +import java.awt.Container; import java.awt.Graphics; import java.awt.Image; import java.awt.Point; @@ -69,6 +70,13 @@ public class SwingButtonPeer extends JButton implements SwingComponent { + Button button; + + SwingButton(Button button) + { + this.button = button; + } + /** * Overridden so that this method returns the correct value even without a * peer. @@ -90,8 +98,8 @@ public class SwingButtonPeer public boolean isShowing() { boolean retVal = false; - if (SwingButtonPeer.this.awtComponent != null) - retVal = SwingButtonPeer.this.awtComponent.isShowing(); + if (button != null) + retVal = button.isShowing(); return retVal; } @@ -168,6 +176,14 @@ public class SwingButtonPeer ev.setSource(this); processKeyEvent(ev); } + + public Container getParent() + { + Container par = null; + if (button != null) + par = button.getParent(); + return par; + } } /** @@ -205,7 +221,7 @@ public class SwingButtonPeer */ public SwingButtonPeer(Button theButton) { - SwingButton button = new SwingButton(); + SwingButton button = new SwingButton(theButton); button.setText(theButton.getLabel()); button.addActionListener(new SwingButtonListener()); init(theButton, button); diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingComponent.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingComponent.java index a51b758adf0..04ca7294f78 100644 --- a/libjava/classpath/gnu/java/awt/peer/swing/SwingComponent.java +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingComponent.java @@ -62,7 +62,7 @@ public interface SwingComponent /** * Handles a mouse event. This is usually forwarded to - * {@link java.awt.Component#processMouseMotionEvent(MouseEvent)} of the swing + * {@link Component#processMouseMotionEvent(MouseEvent)} of the swing * component. * * @param ev the mouse event @@ -71,7 +71,7 @@ public interface SwingComponent /** * Handles a mouse motion event. This is usually forwarded to - * {@link java.awt.Component#processMouseEvent(MouseEvent)} of the swing + * {@link Component#processMouseEvent(MouseEvent)} of the swing * component. * * @param ev the mouse motion event @@ -80,7 +80,7 @@ public interface SwingComponent /** * Handles a key event. This is usually forwarded to - * {@link java.awt.Component#processKeyEvent(KeyEvent)} of the swing + * {@link Component#processKeyEvent(KeyEvent)} of the swing * component. * * @param ev the key event diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingComponentPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingComponentPeer.java index 96ccc00b8f0..bfa14dddee3 100644 --- a/libjava/classpath/gnu/java/awt/peer/swing/SwingComponentPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingComponentPeer.java @@ -42,6 +42,7 @@ import java.awt.AWTException; import java.awt.BufferCapabilities; import java.awt.Color; import java.awt.Component; +import java.awt.Container; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Font; @@ -62,6 +63,10 @@ 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; + +import javax.swing.JComponent; +import javax.swing.RepaintManager; /** * The base class for Swing based component peers. This provides the basic @@ -97,9 +102,18 @@ public class SwingComponentPeer protected SwingComponent swingComponent; /** + * The font that is set for this peer. + */ + protected Font peerFont; + + /** + * The current repaint area. + */ + protected Rectangle paintArea; + + /** * Creates a SwingComponentPeer instance. Subclasses are expected to call - * this constructor and thereafter call - * {@link #init(Component,SwingComponent)} + * this constructor and thereafter call {@link #init(Component, JComponent)} * in order to setup the AWT and Swing components properly. */ protected SwingComponentPeer() @@ -118,6 +132,38 @@ public class SwingComponentPeer { awtComponent = awtComp; swingComponent = swingComp; + if (swingComponent != null) + { + JComponent c = swingComponent.getJComponent(); + if (c != null) + { + c.addNotify(); + RepaintManager.currentManager(c).setDoubleBufferingEnabled(false); + System.setProperty("gnu.awt.swing.doublebuffering", "true"); + } + } + + // Register this heavyweight component with the nearest heavyweight + // container, so we get peerPaint() triggered by that container. + if (! (this instanceof LightweightPeer)) + { + Component comp = awtComponent; + Container parent = comp.getParent(); + while (parent != null && + ! (parent.getPeer() instanceof SwingContainerPeer)) + { + comp = parent; + parent = comp.getParent(); + } + + // At this point we have the ancestor with a SwingContainerPeer + // (or null peer). + if (parent != null && parent.getPeer() instanceof SwingContainerPeer) + { + SwingContainerPeer p = (SwingContainerPeer) parent.getPeer(); + p.addHeavyweightDescendent(awtComponent); + } + } } /** @@ -185,6 +231,28 @@ public class SwingComponentPeer */ public void dispose() { + // Unregister this heavyweight component from the nearest heavyweight + // container. + if (! (this instanceof LightweightPeer)) + { + Component comp = awtComponent; + Container parent = comp.getParent(); + while (parent != null && + ! (parent.getPeer() instanceof SwingContainerPeer)) + { + comp = parent; + parent = comp.getParent(); + } + + // At this point we have the ancestor with a SwingContainerPeer + // (or null peer). + if (parent != null && parent.getPeer() instanceof SwingContainerPeer) + { + SwingContainerPeer p = (SwingContainerPeer) parent.getPeer(); + p.removeHeavyweightDescendent(awtComponent); + } + } + awtComponent = null; swingComponent = null; } @@ -244,8 +312,7 @@ public class SwingComponentPeer public Graphics getGraphics() { Component parent = awtComponent.getParent(); - ComponentPeer parentPeer = parent.getPeer(); - Graphics g = parentPeer.getGraphics(); + Graphics g = parent.getGraphics(); g.translate(awtComponent.getX(), awtComponent.getY()); g.setClip(0, 0, awtComponent.getWidth(), awtComponent.getHeight()); return g; @@ -331,23 +398,28 @@ public class SwingComponentPeer { case PaintEvent.UPDATE: case PaintEvent.PAINT: - // This only will work when the component is showing. - if (awtComponent.isShowing()) + // Need to synchronize to avoid threading problems on the + // paint event list. + // We must synchronize on the tree lock first to avoid deadlock, + // because Container.paint() will grab it anyway. + synchronized (this) { - Graphics g = getGraphics(); - Rectangle clip = ((PaintEvent)e).getUpdateRect(); - g.clipRect(clip.x, clip.y, clip.width, clip.height); - //if (this instanceof LightweightPeer) - // { - if (e.getID() == PaintEvent.UPDATE) - awtComponent.update(g); - else - awtComponent.paint(g); - // } - // We paint the 'heavyweights' at last, so that they appear on top of - // everything else. - peerPaint(g); - g.dispose(); + assert paintArea != null; + if (awtComponent.isShowing()) + { + Graphics g = awtComponent.getGraphics(); + try + { + Rectangle clip = paintArea; + g.clipRect(clip.x, clip.y, clip.width, clip.height); + peerPaint(g, e.getID() == PaintEvent.UPDATE); + } + finally + { + g.dispose(); + paintArea = null; + } + } } break; case MouseEvent.MOUSE_PRESSED: @@ -451,9 +523,15 @@ public class SwingComponentPeer return retVal; } + /** + * Paints the component. This is triggered by + * {@link Component#paintAll(Graphics)}. + * + * @param graphics the graphics to paint with + */ public void paint(Graphics graphics) { - // FIXME: I don't know what this method is supposed to do. + peerPaint(graphics, false); } /** @@ -634,6 +712,7 @@ public class SwingComponentPeer */ public void setFont(Font font) { + peerFont = font; if (swingComponent != null) swingComponent.getJComponent().setFont(font); } @@ -741,7 +820,14 @@ public class SwingComponentPeer */ public void coalescePaintEvent(PaintEvent e) { - // Nothing to do here yet. + synchronized (this) + { + Rectangle newRect = e.getUpdateRect(); + if (paintArea == null) + paintArea = newRect; + else + Rectangle.union(paintArea, newRect, paintArea); + } } /** @@ -947,9 +1033,33 @@ public class SwingComponentPeer * paint() on the Swing component. * * @param g the graphics context to use for painting + * @param update wether we need to call update or paint on the AWT component + */ + protected void peerPaint(Graphics g, boolean update) + { + peerPaintComponent(g); + + Graphics userGraphics = g.create(); + try{ + if (update) + awtComponent.update(userGraphics); + else + awtComponent.paint(userGraphics); + } finally { + userGraphics.dispose(); + } + + } + + /** + * Paints the actual 'heavyweight' swing component, if there is one + * associated to this peer. + * + * @param g the graphics to paint the component with */ - protected void peerPaint(Graphics g) + protected void peerPaintComponent(Graphics g) { + // Paint the actual Swing component if this peer has one. if (swingComponent != null) swingComponent.getJComponent().paint(g); } diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingContainerPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingContainerPeer.java index f433e1b5c2d..c78b644a765 100644 --- a/libjava/classpath/gnu/java/awt/peer/swing/SwingContainerPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingContainerPeer.java @@ -37,14 +37,20 @@ exception statement from your version. */ package gnu.java.awt.peer.swing; +import gnu.classpath.SystemProperties; + import java.awt.Component; import java.awt.Container; import java.awt.Graphics; +import java.awt.Image; import java.awt.Insets; -import java.awt.Shape; +import java.awt.Rectangle; +import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.peer.ComponentPeer; import java.awt.peer.ContainerPeer; +import java.util.Iterator; +import java.util.LinkedList; /** * A peer for Container to be used with the Swing based AWT peers. @@ -57,13 +63,53 @@ public class SwingContainerPeer { /** + * Stores all heavyweight descendents of the container. This is used + * in {@link #peerPaintChildren(Graphics)}. + */ + private LinkedList heavyweightDescendents; + + /** + * The backbuffer used for painting UPDATE events. + */ + private Image backbuffer; + + /** * Creates a new SwingContainerPeer. * * @param awtCont */ - public SwingContainerPeer(Component awtCont) + public SwingContainerPeer(Container awtCont) + { + heavyweightDescendents = new LinkedList(); + } + + /** + * Registers a heavyweight descendent. This is then painted by + * {@link #peerPaintChildren(Graphics)}. + * + * @param comp the descendent to register + * + * @see #peerPaintChildren(Graphics) + * @see #removeHeavyweightDescendent(Component) + */ + synchronized void addHeavyweightDescendent(Component comp) + { + heavyweightDescendents.add(comp); + focusOwner = null; + } + + /** + * Unregisters a heavyweight descendent. + * + * @param comp the descendent to unregister + * + * @see #peerPaintChildren(Graphics) + * @see #addHeavyweightDescendent(Component) + */ + synchronized void removeHeavyweightDescendent(Component comp) { - init(awtCont, null); + heavyweightDescendents.remove(comp); + focusOwner = null; } /** @@ -92,12 +138,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(); } /** @@ -171,38 +212,83 @@ public class SwingContainerPeer } /** - * Triggers painting of a component. This calls peerPaint on all the child - * components of this container. - * - * @param g the graphics context to paint to + * Performs the super behaviour (call peerPaintComponent() and + * awtComponent.paint()), and forwards the paint request to the heavyweight + * descendents of the container. */ - protected void peerPaint(Graphics g) + protected void peerPaint(Graphics g, boolean update) { - Container c = (Container) awtComponent; - Component[] children = c.getComponents(); - for (int i = children.length - 1; i >= 0; --i) + if (isDoubleBuffering()) { - Component child = children[i]; - ComponentPeer peer = child.getPeer(); - boolean translated = false; - boolean clipped = false; - Shape oldClip = g.getClip(); + int width = awtComponent.getWidth(); + int height = awtComponent.getHeight(); + if (backbuffer == null + || backbuffer.getWidth(awtComponent) < width + || backbuffer.getHeight(awtComponent) < height) + backbuffer = awtComponent.createImage(width, height); + Graphics g2 = backbuffer.getGraphics(); + Rectangle clip = g.getClipRect(); try - { - g.translate(child.getX(), child.getY()); - translated = true; - g.setClip(0, 0, child.getWidth(), child.getHeight()); - clipped = true; - if (peer instanceof SwingComponentPeer) - ((SwingComponentPeer) peer).peerPaint(g); - } + { + g2.setClip(clip); + super.peerPaint(g2, update); + peerPaintChildren(g2); + } finally - { - if (translated) - g.translate(- child.getX(), - child.getY()); - if (clipped) - g.setClip(oldClip); - } + { + g2.dispose(); + } + g.drawImage(backbuffer, 0, 0, awtComponent); + } + else + { + super.peerPaint(g, update); + peerPaintChildren(g); + } + } + + /** + * Determines if we should do double buffering or not. + * + * @return if we should do double buffering or not + */ + private boolean isDoubleBuffering() + { + Object prop = + SystemProperties.getProperty("gnu.awt.swing.doublebuffering", "false"); + return prop.equals("true"); + } + + /** + * Paints any heavyweight child components. + * + * @param g the graphics to use for painting + */ + protected synchronized void peerPaintChildren(Graphics g) + { + // TODO: Is this the right painting order? + for (Iterator i = heavyweightDescendents.iterator(); i.hasNext();) + { + Component child = (Component) i.next(); + ComponentPeer peer = child.getPeer(); + + if (peer instanceof SwingComponentPeer && child.isVisible()) + { + // TODO: The translation here doesn't work for deeper + // nested children. Fix this! + Graphics g2 = g.create(child.getX(), child.getY(), + child.getWidth(), child.getHeight()); + try + { + // update() is only called for the topmost component if + // necessary, all other components only get paint() called. + ((SwingComponentPeer) peer).peerPaint(g2, false); + } + finally + { + g2.dispose(); + } + } } } @@ -214,17 +300,13 @@ public class SwingContainerPeer protected void handleMouseEvent(MouseEvent ev) { Component comp = awtComponent.getComponentAt(ev.getPoint()); - if(comp == null) - comp = awtComponent; - if (comp != null) + if(comp == null) comp = awtComponent; + ComponentPeer peer = comp.getPeer(); + if (awtComponent != comp && !comp.isLightweight() && peer instanceof SwingComponentPeer) { - 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); - } + ev.translatePoint(comp.getX(), comp.getY()); + ev.setSource(comp); + ((SwingComponentPeer) peer).handleMouseEvent(ev); } } @@ -246,4 +328,39 @@ public class SwingContainerPeer } } } + + /** + * Handles key events on the component. This is usually forwarded to the + * SwingComponent's processKeyEvent() method. + * + * @param e the key event + */ + protected void handleKeyEvent(KeyEvent e) + { + Component owner = getFocusOwner(); + if(owner != null) + owner.dispatchEvent(e); + else + super.handleKeyEvent(e); + } + + private Component focusOwner = null; + + private Component getFocusOwner() + { + if(focusOwner == null) + { + for(Iterator iter=heavyweightDescendents.iterator(); iter.hasNext();) + { + Component child = (Component) iter.next(); + if(child.isFocusable()) + { + focusOwner = child; + break; + } + } + } + return focusOwner; + } + } diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingFramePeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingFramePeer.java index 0d5a02d78f6..56c7417cdcf 100644 --- a/libjava/classpath/gnu/java/awt/peer/swing/SwingFramePeer.java +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingFramePeer.java @@ -43,6 +43,7 @@ import java.awt.Insets; import java.awt.MenuBar; import java.awt.Point; import java.awt.event.MouseEvent; +import java.awt.peer.ComponentPeer; import java.awt.peer.FramePeer; /** @@ -53,9 +54,9 @@ import java.awt.peer.FramePeer; * As a minimum, a subclass must implement all the remaining abstract methods * as well as the following methods: * <ul> - * <li>{@link java.awt.peer.ComponentPeer#getLocationOnScreen()}</li> - * <li>{@link java.awt.peer.ComponentPeer#getGraphics()}</li> - * <li>{@link java.awt.peer.ComponentPeer#createImage(int, int)}</li> + * <li>{@link ComponentPeer#getLocationOnScreen()}</li> + * <li>{@link ComponentPeer#getGraphics()}</li> + * <li>{@link ComponentPeer#createImage(int, int)}</li> * </ul> * * @author Roman Kennke (kennke@aicas.com) @@ -97,9 +98,9 @@ public abstract class SwingFramePeer * * @param g the graphics context to use for painting */ - protected void peerPaint(Graphics g) + protected void peerPaintComponent(Graphics g) { - super.peerPaint(g); + super.peerPaintComponent(g); if (menuBar != null) menuBar.peerPaint(g); } diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingLabelPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingLabelPeer.java index dd86fff2d10..349c5a0abe2 100644 --- a/libjava/classpath/gnu/java/awt/peer/swing/SwingLabelPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingLabelPeer.java @@ -37,6 +37,8 @@ exception statement from your version. */ package gnu.java.awt.peer.swing; +import java.awt.Container; +import java.awt.Graphics; import java.awt.Image; import java.awt.Label; import java.awt.Point; @@ -67,7 +69,14 @@ public class SwingLabelPeer extends JLabel implements SwingComponent { - + Label label; + + + SwingLabel(Label label) + { + this.label = label; + } + /** * Returns this label. * @@ -131,8 +140,8 @@ public class SwingLabelPeer public boolean isShowing() { boolean retVal = false; - if (SwingLabelPeer.this.awtComponent != null) - retVal = SwingLabelPeer.this.awtComponent.isShowing(); + if (label != null) + retVal = label.isShowing(); return retVal; } @@ -149,7 +158,19 @@ public class SwingLabelPeer { return SwingLabelPeer.this.createImage(w, h); } - + + public Graphics getGraphics() + { + return SwingLabelPeer.this.getGraphics(); + } + + public Container getParent() + { + Container par = null; + if (label != null) + par = label.getParent(); + return par; + } } /** @@ -160,11 +181,11 @@ public class SwingLabelPeer public SwingLabelPeer(Label label) { super(); - SwingLabel swingLabel = new SwingLabel(); + SwingLabel swingLabel = new SwingLabel(label); swingLabel.setText(label.getText()); - swingLabel.setHorizontalAlignment(label.getAlignment()); swingLabel.setOpaque(true); init(label, swingLabel); + setAlignment(label.getAlignment()); } /** @@ -190,7 +211,20 @@ public class SwingLabelPeer */ public void setAlignment(int alignment) { - ((JLabel) swingComponent.getJComponent()).setHorizontalAlignment(alignment); + JLabel swingLabel = (JLabel) swingComponent.getJComponent(); + switch (alignment) + { + case Label.RIGHT: + swingLabel.setHorizontalAlignment(JLabel.RIGHT); + break; + case Label.CENTER: + swingLabel.setHorizontalAlignment(JLabel.CENTER); + break; + case Label.LEFT: + default: + swingLabel.setHorizontalAlignment(JLabel.LEFT); + break; + } } } diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingListPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingListPeer.java new file mode 100644 index 00000000000..aca2070486d --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingListPeer.java @@ -0,0 +1,352 @@ +/* SwingListPeer.java -- A Swing based peer for AWT lists + 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.swing; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.List; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.peer.ListPeer; + +import javax.swing.DefaultListModel; +import javax.swing.JComponent; +import javax.swing.JList; +import javax.swing.JScrollPane; +import javax.swing.ListSelectionModel; + +public class SwingListPeer + extends SwingComponentPeer + implements ListPeer +{ + + /** + * A spezialized Swing scroller used to hold the list. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class SwingList + extends JScrollPane + implements SwingComponent + { + + SwingList(Component comp) + { + super(comp, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + } + + /** + * Returns this label. + * + * @return <code>this</code> + */ + public JComponent getJComponent() + { + return this; + } + + /** + * Handles mouse events by forwarding it to + * <code>processMouseEvent()</code>. + * + * @param ev the mouse event + */ + public void handleMouseEvent(MouseEvent ev) + { + ev.setSource(this); + dispatchEvent(ev); + } + + /** + * Force lightweight mouse dispatching. + */ + public boolean isLightweight() + { + return false; + } + + /** + * Handles mouse motion events by forwarding it to + * <code>processMouseMotionEvent()</code>. + * + * @param ev the mouse motion event + */ + public void handleMouseMotionEvent(MouseEvent ev) + { + processMouseMotionEvent(ev); + } + + /** + * Handles key events by forwarding it to <code>processKeyEvent()</code>. + * + * @param ev the mouse event + */ + public void handleKeyEvent(KeyEvent ev) + { + processKeyEvent(ev); + } + + /** + * Overridden so that this method returns the correct value even without a + * peer. + * + * @return the screen location of the button + */ + public Point getLocationOnScreen() + { + return SwingListPeer.this.getLocationOnScreen(); + } + + /** + * Overridden so that the isShowing method returns the correct value for the + * swing button, even if it has no peer on its own. + * + * @return <code>true</code> if the button is currently showing, + * <code>false</code> otherwise + */ + public boolean isShowing() + { + boolean retVal = false; + if (SwingListPeer.this.awtComponent != null) + retVal = SwingListPeer.this.awtComponent.isShowing(); + return retVal; + } + + /** + * Overridden, so that the Swing button can create an Image without its + * own peer. + * + * @param w the width of the image + * @param h the height of the image + * + * @return an image + */ + public Image createImage(int w, int h) + { + return SwingListPeer.this.createImage(w, h); + } + + public Graphics getGraphics() + { + return SwingListPeer.this.getGraphics(); + } + + public Container getParent() + { + Container par = null; + if (SwingListPeer.this.awtComponent != null) + par = SwingListPeer.this.awtComponent.getParent(); + return par; + } + } + + /** + * The actual Swing JList. + */ + private JList jList; + + private DefaultListModel listModel; + + public SwingListPeer(List list) + { + super(); + listModel = new DefaultListModel(); + jList = new JList(listModel); + SwingList swingList = new SwingList(jList); + init(list, swingList); + + // Pull over the items from the list. + String[] items = list.getItems(); + for (int i = 0 ; i < items.length; i++) + addItem(items[i], i); + } + + public void add(String item, int index) + { + if (listModel != null) + listModel.add(index, item); + } + + public void addItem(String item, int index) + { + if (listModel != null) + listModel.add(index, item); + } + + public void clear() + { + if (listModel != null) + listModel.clear(); + } + + public void delItems(int startIndex, int endIndex) + { + if (listModel != null) + listModel.removeRange(startIndex, endIndex); + } + + public void deselect(int index) + { + if (jList != null) + { + jList.getSelectionModel().removeSelectionInterval(index, index); + } + } + + public Dimension getMinimumSize(int s) + { + Dimension d = null; + if (jList != null) + { + d = jList.getComponent(s).getMinimumSize(); + } + return d; + } + + public Dimension getPreferredSize(int s) + { + Dimension d = null; + if (jList != null) + { + d = jList.getComponent(s).getPreferredSize(); + } + return d; + } + + public int[] getSelectedIndexes() + { + int[] sel = null; + if (jList != null) + { + sel = jList.getSelectedIndices(); + } + return sel; + } + + public void makeVisible(int index) + { + if (jList != null) + { + Component comp = jList.getComponent(index); + jList.scrollRectToVisible(comp.getBounds()); + } + } + + public Dimension minimumSize(int s) + { + Dimension d = null; + if (jList != null) + { + d = jList.getComponent(s).getMinimumSize(); + } + return d; + } + + public Dimension preferredSize(int s) + { + Dimension d = null; + if (jList != null) + { + d = jList.getComponent(s).getPreferredSize(); + } + return d; + } + + public void removeAll() + { + if (jList != null) + { + jList.removeAll(); + } + } + + public void select(int index) + { + if (jList != null) + { + jList.setSelectedIndex(index); + } + } + + public void setMultipleMode(boolean multi) + { + if (jList != null) + { + jList.setSelectionMode(multi + ? ListSelectionModel.MULTIPLE_INTERVAL_SELECTION + : ListSelectionModel.SINGLE_SELECTION); + } + } + + public void setMultipleSelections(boolean multi) + { + if (jList != null) + { + jList.setSelectionMode(multi + ? ListSelectionModel.MULTIPLE_INTERVAL_SELECTION + : ListSelectionModel.SINGLE_SELECTION); + } + } + + public void reshape(int x, int y, int width, int height) + { + if (swingComponent != null) + { + swingComponent.getJComponent().setBounds(x, y, width, height); + swingComponent.getJComponent().validate(); + } + } + + protected void peerPaint(Graphics g, boolean update) + { + super.peerPaint(g, update); + jList.doLayout(); + jList.list(); + + Rectangle r = getBounds(); + g.setColor(Color.RED); + g.drawRect(r.x, r.y, r.width, r.height); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingMenuBarPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingMenuBarPeer.java index 0033efb025f..bd9dcd77aaa 100644 --- a/libjava/classpath/gnu/java/awt/peer/swing/SwingMenuBarPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingMenuBarPeer.java @@ -174,7 +174,7 @@ public class SwingMenuBarPeer /** * Adds a help menu to the menu bar. * - * @param menu the menu to add + * @param m the menu to add */ public void addHelpMenu(Menu menu) { diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingPanelPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingPanelPeer.java index 0a0f20fe826..3cea62ac4d1 100644 --- a/libjava/classpath/gnu/java/awt/peer/swing/SwingPanelPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingPanelPeer.java @@ -39,7 +39,6 @@ exception statement from your version. */ package gnu.java.awt.peer.swing; import java.awt.Panel; -import java.awt.peer.LightweightPeer; import java.awt.peer.PanelPeer; /** @@ -51,7 +50,7 @@ import java.awt.peer.PanelPeer; // necessary, but might be good for more consistend Look. public class SwingPanelPeer extends SwingContainerPeer - implements PanelPeer, LightweightPeer + implements PanelPeer { /** @@ -63,5 +62,6 @@ public class SwingPanelPeer public SwingPanelPeer(Panel panel) { super(panel); + init(panel, null); } } diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingTextAreaPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingTextAreaPeer.java new file mode 100644 index 00000000000..04ac011419f --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingTextAreaPeer.java @@ -0,0 +1,317 @@ +/* SwingTextAreaPeer.java -- A Swing based peer for AWT textareas + 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.swing; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.TextArea; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.im.InputMethodRequests; +import java.awt.peer.TextAreaPeer; + +import javax.swing.JComponent; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.text.BadLocationException; + +public class SwingTextAreaPeer + extends SwingComponentPeer + implements TextAreaPeer +{ + + /** + * A spezialized Swing scroller used to hold the textarea. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class SwingTextArea + extends JScrollPane + implements SwingComponent + { + + SwingTextArea(Component comp) + { + super(comp, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + } + + /** + * Returns this label. + * + * @return <code>this</code> + */ + public JComponent getJComponent() + { + return this; + } + + /** + * Handles mouse events by forwarding it to + * <code>processMouseEvent()</code>. + * + * @param ev the mouse event + */ + public void handleMouseEvent(MouseEvent ev) + { + ev.setSource(this); + dispatchEvent(ev); + } + + /** + * Force lightweight mouse dispatching. + */ + public boolean isLightweight() + { + return false; + } + + /** + * Handles mouse motion events by forwarding it to + * <code>processMouseMotionEvent()</code>. + * + * @param ev the mouse motion event + */ + public void handleMouseMotionEvent(MouseEvent ev) + { + processMouseMotionEvent(ev); + } + + /** + * Handles key events by forwarding it to <code>processKeyEvent()</code>. + * + * @param ev the mouse event + */ + public void handleKeyEvent(KeyEvent ev) + { + processKeyEvent(ev); + } + + /** + * Overridden so that this method returns the correct value even without a + * peer. + * + * @return the screen location of the button + */ + public Point getLocationOnScreen() + { + return SwingTextAreaPeer.this.getLocationOnScreen(); + } + + /** + * Overridden so that the isShowing method returns the correct value for the + * swing button, even if it has no peer on its own. + * + * @return <code>true</code> if the button is currently showing, + * <code>false</code> otherwise + */ + public boolean isShowing() + { + boolean retVal = false; + if (SwingTextAreaPeer.this.awtComponent != null) + retVal = SwingTextAreaPeer.this.awtComponent.isShowing(); + return retVal; + } + + /** + * Overridden, so that the Swing button can create an Image without its + * own peer. + * + * @param w the width of the image + * @param h the height of the image + * + * @return an image + */ + public Image createImage(int w, int h) + { + return SwingTextAreaPeer.this.createImage(w, h); + } + + public Graphics getGraphics() + { + return SwingTextAreaPeer.this.getGraphics(); + } + + public Container getParent() + { + Container par = null; + if (SwingTextAreaPeer.this.awtComponent != null) + par = SwingTextAreaPeer.this.awtComponent.getParent(); + return par; + } + } + + /** + * The actual JTextArea. + */ + private JTextArea jTextArea; + + public SwingTextAreaPeer(TextArea textArea) + { + super(); + System.err.println("new SwingTextAreaPeer"); + jTextArea = new JTextArea(); + SwingTextArea swingArea = new SwingTextArea(jTextArea); + init(textArea, swingArea); + + // Pull over the text from the text area. + setText(textArea.getText()); + } + + public Dimension getMinimumSize(int rows, int cols) + { + // TODO Auto-generated method stub + return null; + } + + public Dimension getPreferredSize(int rows, int cols) + { + // TODO Auto-generated method stub + return null; + } + + public void insert(String text, int pos) + { + jTextArea.insert(text, pos); + } + + public void insertText(String text, int pos) + { + jTextArea.insert(text, pos); + } + + public Dimension minimumSize(int rows, int cols) + { + // TODO Auto-generated method stub + return null; + } + + public Dimension preferredSize(int rows, int cols) + { + // TODO Auto-generated method stub + return null; + } + + public void replaceRange(String text, int start, int end) + { + jTextArea.replaceRange(text, start, end); + } + + public void replaceText(String text, int start, int end) + { + jTextArea.replaceRange(text, start, end); + } + + public long filterEvents(long filter) + { + // TODO Auto-generated method stub + return 0; + } + + public int getCaretPosition() + { + return jTextArea.getCaretPosition(); + } + + public Rectangle getCharacterBounds(int pos) + { + Rectangle r; + try + { + return jTextArea.modelToView(pos); + } + catch (BadLocationException ex) + { + r = null; + } + return r; + } + + public int getIndexAtPoint(int x, int y) + { + return jTextArea.viewToModel(new Point(x, y)); + } + + public InputMethodRequests getInputMethodRequests() + { + // TODO Auto-generated method stub + return null; + } + + public int getSelectionEnd() + { + return jTextArea.getSelectionEnd(); + } + + public int getSelectionStart() + { + return jTextArea.getSelectionStart(); + } + + public String getText() + { + return jTextArea.getText(); + } + + public void select(int start, int end) + { + jTextArea.select(start, end); + } + + public void setCaretPosition(int pos) + { + jTextArea.setCaretPosition(pos); + } + + public void setEditable(boolean editable) + { + jTextArea.setEditable(editable); + } + + public void setText(String text) + { + System.err.println("setText: " + text); + jTextArea.setText(text); + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingTextFieldPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingTextFieldPeer.java index 0c3b4e72603..d7d574a0bb9 100644 --- a/libjava/classpath/gnu/java/awt/peer/swing/SwingTextFieldPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingTextFieldPeer.java @@ -36,7 +36,10 @@ obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ package gnu.java.awt.peer.swing; +import java.awt.Component; +import java.awt.Container; import java.awt.Dimension; +import java.awt.Graphics; import java.awt.Image; import java.awt.Point; import java.awt.Rectangle; @@ -69,6 +72,13 @@ public class SwingTextFieldPeer implements SwingComponent { + TextField textField; + + SwingTextField(TextField textField) + { + this.textField = textField; + } + /** * Overridden to provide normal behaviour even without a real peer * attached. @@ -90,8 +100,8 @@ public class SwingTextFieldPeer public boolean isShowing() { boolean retVal = false; - if (SwingTextFieldPeer.this.awtComponent != null) - retVal = SwingTextFieldPeer.this.awtComponent.isShowing(); + if (textField != null) + retVal = textField.isShowing(); return retVal; } @@ -151,7 +161,19 @@ public class SwingTextFieldPeer ev.setSource(this); processKeyEvent(ev); } - + + public Container getParent() + { + Container par = null; + if (textField != null) + par = textField.getParent(); + return par; + } + + public Graphics getGraphics() + { + return SwingTextFieldPeer.this.getGraphics(); + } } /** @@ -162,7 +184,7 @@ public class SwingTextFieldPeer */ public SwingTextFieldPeer(TextField textField) { - SwingTextField swingTextField = new SwingTextField(); + SwingTextField swingTextField = new SwingTextField(textField); swingTextField.setText(textField.getText()); init(textField, swingTextField); } @@ -283,7 +305,7 @@ public class SwingTextFieldPeer * @param startPos the start index of the selection * @param endPos the start index of the selection */ - public void select(int startPos, int endPos) + public void select(int start_pos, int endPos) { // TODO: Must be implemented. } diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingWindowPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingWindowPeer.java index 43a509b957d..531552d902c 100644 --- a/libjava/classpath/gnu/java/awt/peer/swing/SwingWindowPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingWindowPeer.java @@ -38,6 +38,7 @@ exception statement from your version. */ package gnu.java.awt.peer.swing; import java.awt.Window; +import java.awt.peer.ComponentPeer; import java.awt.peer.WindowPeer; /** @@ -48,9 +49,9 @@ import java.awt.peer.WindowPeer; * As a minimum, a subclass must implement all the remaining abstract methods * as well as the following methods: * <ul> - * <li>{@link java.awt.peer.ComponentPeer#getLocationOnScreen()}</li> - * <li>{@link java.awt.peer.ComponentPeer#getGraphics()}</li> - * <li>{@link java.awt.peer.ComponentPeer#createImage(int, int)}</li> + * <li>{@link ComponentPeer#getLocationOnScreen()}</li> + * <li>{@link ComponentPeer#getGraphics()}</li> + * <li>{@link ComponentPeer#createImage(int, int)}</li> * </ul> * * @author Roman Kennke (kennke@aicas.com) @@ -68,5 +69,6 @@ public abstract class SwingWindowPeer public SwingWindowPeer(Window window) { super(window); + init(window, null); } } diff --git a/libjava/classpath/gnu/java/awt/peer/x/XFontPeer.java b/libjava/classpath/gnu/java/awt/peer/x/XFontPeer.java index fd293d8dd43..8183fed0cd2 100644 --- a/libjava/classpath/gnu/java/awt/peer/x/XFontPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/x/XFontPeer.java @@ -608,13 +608,6 @@ public class XFontPeer throw new UnsupportedOperationException("Not yet implemented."); } - public Rectangle2D getStringBounds(Font font, CharacterIterator ci, - int begin, int limit, FontRenderContext frc) - { - // TODO: Implement this. - throw new UnsupportedOperationException("Not yet implemented."); - } - /** * Encodes a font name + style + size specification into a X logical font * description (XLFD) as described here: diff --git a/libjava/classpath/gnu/java/awt/peer/x/XFontPeer2.java b/libjava/classpath/gnu/java/awt/peer/x/XFontPeer2.java index 25371de1a41..ef9507f3050 100644 --- a/libjava/classpath/gnu/java/awt/peer/x/XFontPeer2.java +++ b/libjava/classpath/gnu/java/awt/peer/x/XFontPeer2.java @@ -326,10 +326,4 @@ public class XFontPeer2 throw new UnsupportedOperationException("Not yet implemented"); } - public Rectangle2D getStringBounds(Font font, CharacterIterator ci, int begin, int limit, FontRenderContext frc) - { - // FIXME: Implement this. - throw new UnsupportedOperationException("Not yet implemented"); - } - } diff --git a/libjava/classpath/gnu/java/io/ObjectIdentityMap2Int.java b/libjava/classpath/gnu/java/io/ObjectIdentityMap2Int.java new file mode 100644 index 00000000000..08f089d7921 --- /dev/null +++ b/libjava/classpath/gnu/java/io/ObjectIdentityMap2Int.java @@ -0,0 +1,292 @@ +/* ObjectIdentityMapToInt.java -- Helper class for faster serialization + 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.io; + +/** + * This class provides a map from Object to non-negative int values. + * Objects are considered equal only if their references are equal. + * + * This can be used to equip objects with an integer id. This class + * is implemented to use as little memory as possible, particularly + * not to create hashtable buckets and Integer instances for each + * mapping. + * + * @author Fridtjof Siebert (siebert@aicas.com) + */ +public class ObjectIdentityMap2Int +{ + + + /** + * Prime numbers used as size of array. We need the size to be a + * prime number since the delta used for conflict resulution must + * not have any common divisors with the length. + */ + private static final int[] PRIMES = { + 0x1f, + 0x3d, + 0x7f, + 0xfb, + 0x1fd, + 0x3fd, + 0x7f7, + 0xffd, + 0x1fff, + 0x3ffd, + 0x7fed, + 0xfff1, + 0x1ffff, + 0x3fffb, + 0x7ffff, + 0xffffd, + 0x1ffff7, + 0x3ffffd, + 0x7ffff1, + 0xfffffd, + 0x1ffffd9, + 0x3fffffb, + 0x7ffffd9, + 0xfffffc7, + 0x1ffffffd, + 0x3fffffdd, + 0x7fffffff}; + + + /** + * Object to be used instead of "null" + */ + private static final Object NIL = new Object(); + + + /** + * The objects in this map: + * + * invariant + * objectTable.size == PRIMES[cap] + */ + private Object[] objectTable; + + + /** + * The corresponding integer ids. + * + * invariant + * intTable.size == PRIMES[cap] + */ + private int[] intTable; + + + /** + * The number of entries in this map. + * + * invariant + * size < limit + */ + private int size = 0; + + + /** + * The index in primes of the size of the tables. + */ + private int cap = 0; + + + /** + * The limit for size at which the table size is increased. + * + * invariant + * limit = PRIMES[cap] / 4 * 3; + */ + private int limit = 0; + + + /** + * Constructs an empty <code>ObjectIdentityMap2Int</code>. + */ + public ObjectIdentityMap2Int() + { + alloc(0); + } + + + /** + * Helper function to alloc the object and int array for the given + * capacity. Set limit, reset size to 0. + * + * No elements will be stored in the newly allocated arrays. + * + * @param c the capacity: this is an index in PRIMES, PRIMES[c] + * gives the size of the arrays. + * + * @throws InternalError if c >= PRIMES.length (in this case, a + * normal Hashtable would throw an OutOfMemoryError or a + * NegativeArraySizeException since the array size exceeds the range + * of positive integers). + */ + private void alloc(int c) + { + if (c >= PRIMES.length) + throw new InternalError("Hash table size overflow"); + + cap = c; + int len = PRIMES[c]; + objectTable = new Object[len]; + intTable = new int[len]; + limit = len / 4 * 3; + + size = 0; + } + + + /** + * Add a mapping to this Map. + * + * ensures + * (get(o) == i); + * + * @param o object reference or null that is to be mapped. + * + * @param i the integer id to be associated with o + * + * @throws IllegalArgumentException if i<0 + * + * @throws InternalError if hash tables has grown to more then + * 0x7fffffff entries (ie., size >= 0x7fffffff*3/4). + */ + public void put(Object o, int i) + { + if (i < 0) + throw new IllegalArgumentException("int argument must be postive: "+i); + + o = (o == null) ? NIL : o; + int s = slot(o); + Object[] ot = objectTable; + intTable[s] = i; + if (objectTable[s] == null) + { + objectTable[s] = o; + size++; + if (size >= limit) + { + rehash(); + } + } + } + + + /** + * Helper function to find the index of a free or existing slot for + * object o + * + * ensure + * ((objectTable[result] != null) IMPLIES (objectTable[result] == o)); + * + * @param o an object, must not be null. + * + * @return an index of o + */ + private int slot(Object o) + { + Object[] ot = objectTable; + int hc = System.identityHashCode(o); + int len = ot.length; + int result = hc % len; + result = result < 0 ? -result : result; + int delta = 16 - (hc & 15); + Object existing = ot[result]; + while ((existing != null) && (existing != o)) + { + result += delta; + if (result >= len) + result -= len; + existing = ot[result]; + } + return result; + } + + + /** + * Helper function for put() to increaes the capacity of this table + * to the next size (approx. double the size). Keep the mapping and + * the size unchanged. + * + * ensure + * (cap == \old cap+1); + */ + private void rehash() + { + Object[] ot = objectTable; + int [] it = intTable; + alloc(cap + 1); + + for (int i = 0; i < ot.length; i++) + put(ot[i], it[i]); + } + + + /** + * Obtain an element from this map + * + * @param o an object or null + * + * @return the corresponding integer id for o or -1 if o has not + * been put into this map. + */ + public int get(Object o) + { + o = (o == null) ? NIL : o; + int s = slot(o); + return objectTable[s] == null ? -1 : intTable[s]; + } + + /** + * Clear this map + * + * ensures + * ((size == 0) && \forall Object o: get(o) == -1) + */ + public void clear() + { + Object[] ot = objectTable; + size = 0; + for (int i = 0; i < ot.length; i++) + ot[i] = null; + } + +} diff --git a/libjava/classpath/gnu/java/io/class-dependencies.conf b/libjava/classpath/gnu/java/io/class-dependencies.conf new file mode 100644 index 00000000000..2500f6b14b5 --- /dev/null +++ b/libjava/classpath/gnu/java/io/class-dependencies.conf @@ -0,0 +1,75 @@ +# 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/InstrumentationImpl.java b/libjava/classpath/gnu/java/lang/InstrumentationImpl.java index 7bfed458245..2425b35c8a9 100644 --- a/libjava/classpath/gnu/java/lang/InstrumentationImpl.java +++ b/libjava/classpath/gnu/java/lang/InstrumentationImpl.java @@ -1,6 +1,6 @@ /* InstrumentationImpl.java -- GNU implementation of java.lang.instrument.Instrumentation - Copyright (C) 2005 Free Software Foundation, Inc. + Copyright (C) 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -63,11 +63,11 @@ public final class InstrumentationImpl implements Instrumentation { /* List of transformers */ - /* FIXME[GENERICS]: Should be ClassFileTransformer list */ - private ArrayList transformers = new ArrayList(); + private ArrayList<ClassFileTransformer> transformers = + new ArrayList<ClassFileTransformer>(); - private InstrumentationImpl() + InstrumentationImpl() { } @@ -210,9 +210,8 @@ public final class InstrumentationImpl implements Instrumentation * * @return the new class file */ - /* FIXME[GENERICS]: Should be Class<?> */ public byte[] callTransformers(ClassLoader loader, String className, - Class classBeingRedefined, ProtectionDomain protectionDomain, + Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { byte[] newBuffer = null; @@ -220,11 +219,10 @@ public final class InstrumentationImpl implements Instrumentation ClassFileTransformer current; synchronized (transformers) { - Iterator i = transformers.iterator(); + Iterator<ClassFileTransformer> i = transformers.iterator(); while (i.hasNext()) { - /* FIXME[GENERICS]: Remove cast */ - current = (ClassFileTransformer) i.next(); + current = i.next(); try { newBuffer = current.transform(loader, className, diff --git a/libjava/classpath/gnu/java/lang/management/BeanImpl.java b/libjava/classpath/gnu/java/lang/management/BeanImpl.java index 24572a25d5d..e5b47181273 100644 --- a/libjava/classpath/gnu/java/lang/management/BeanImpl.java +++ b/libjava/classpath/gnu/java/lang/management/BeanImpl.java @@ -39,9 +39,50 @@ package gnu.java.lang.management; import java.lang.management.ManagementPermission; +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.TypeVariable; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.management.AttributeNotFoundException; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanConstructorInfo; +import javax.management.MBeanException; +import javax.management.MBeanInfo; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanParameterInfo; +import javax.management.MBeanInfo; import javax.management.NotCompliantMBeanException; +import javax.management.ReflectionException; import javax.management.StandardMBean; +import javax.management.openmbean.ArrayType; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenMBeanAttributeInfo; +import javax.management.openmbean.OpenMBeanAttributeInfoSupport; +import javax.management.openmbean.OpenMBeanConstructorInfo; +import javax.management.openmbean.OpenMBeanConstructorInfoSupport; +import javax.management.openmbean.OpenMBeanInfo; +import javax.management.openmbean.OpenMBeanInfoSupport; +import javax.management.openmbean.OpenMBeanOperationInfo; +import javax.management.openmbean.OpenMBeanOperationInfoSupport; +import javax.management.openmbean.OpenMBeanParameterInfo; +import javax.management.openmbean.OpenMBeanParameterInfoSupport; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; +import javax.management.openmbean.TabularType; + /** * A common superclass for bean implementations. * @@ -51,7 +92,12 @@ import javax.management.StandardMBean; public class BeanImpl extends StandardMBean { - + + /** + * Cached open bean information. + */ + private OpenMBeanInfo openInfo; + /** * Constructs a new <code>BeanImpl</code>. * @@ -67,6 +113,71 @@ public class BeanImpl super(iface); } + protected void cacheMBeanInfo(MBeanInfo info) + { + if (info == null) + return; + try + { + MBeanAttributeInfo[] oldA = info.getAttributes(); + OpenMBeanAttributeInfo[] attribs = + new OpenMBeanAttributeInfoSupport[oldA.length]; + for (int a = 0; a < oldA.length; ++a) + { + OpenMBeanParameterInfo param = translate(oldA[a].getType()); + if (param.getMinValue() == null) + { + Object[] lv; + if (param.getLegalValues() == null) + lv = null; + else + lv = param.getLegalValues().toArray(); + attribs[a] = new OpenMBeanAttributeInfoSupport(oldA[a].getName(), + oldA[a].getDescription(), + param.getOpenType(), + oldA[a].isReadable(), + oldA[a].isWritable(), + oldA[a].isIs(), + param.getDefaultValue(), + lv); + } + else + attribs[a] = new OpenMBeanAttributeInfoSupport(oldA[a].getName(), + oldA[a].getDescription(), + param.getOpenType(), + oldA[a].isReadable(), + oldA[a].isWritable(), + oldA[a].isIs(), + param.getDefaultValue(), + param.getMinValue(), + param.getMaxValue()); + } + MBeanConstructorInfo[] oldC = info.getConstructors(); + OpenMBeanConstructorInfo[] cons = new OpenMBeanConstructorInfoSupport[oldC.length]; + for (int a = 0; a < oldC.length; ++a) + cons[a] = + new OpenMBeanConstructorInfoSupport(oldC[a].getName(), + oldC[a].getDescription(), + translateSignature(oldC[a].getSignature())); + MBeanOperationInfo[] oldO = info.getOperations(); + OpenMBeanOperationInfo[] ops = new OpenMBeanOperationInfoSupport[oldO.length]; + for (int a = 0; a < oldO.length; ++a) + ops[a] = + new OpenMBeanOperationInfoSupport(oldO[a].getName(), + oldO[a].getDescription(), + translateSignature(oldO[a].getSignature()), + translate(oldO[a].getReturnType()).getOpenType(), + oldO[a].getImpact()); + openInfo = new OpenMBeanInfoSupport(info.getClassName(), info.getDescription(), + attribs, cons, ops, info.getNotifications()); + } + catch (OpenDataException e) + { + throw (InternalError) (new InternalError("A problem occurred creating the open type " + + "descriptors.").initCause(e)); + } + } + protected void checkMonitorPermissions() { SecurityManager sm = System.getSecurityManager(); @@ -81,4 +192,328 @@ public class BeanImpl sm.checkPermission(new ManagementPermission("control")); } + public Object getAttribute(String attribute) + throws AttributeNotFoundException, MBeanException, + ReflectionException + { + Object value = super.getAttribute(attribute); + if (value instanceof Enum) + return ((Enum) value).name(); + Class vClass = value.getClass(); + if (vClass.isArray()) + return value; + String cName = vClass.getName(); + String[] allowedTypes = OpenType.ALLOWED_CLASSNAMES; + for (int a = 0; a < allowedTypes.length; ++a) + if (cName.equals(allowedTypes[a])) + return value; + if (value instanceof List) + { + List l = (List) value; + Class e = null; + TypeVariable[] vars = vClass.getTypeParameters(); + for (int a = 0; a < vars.length; ++a) + if (vars[a].getName().equals("E")) + e = (Class) vars[a].getGenericDeclaration(); + if (e == null) + e = Object.class; + Object[] array = (Object[]) Array.newInstance(e, l.size()); + return l.toArray(array); + } + OpenMBeanInfo info = (OpenMBeanInfo) getMBeanInfo(); + OpenMBeanAttributeInfo[] attribs = + (OpenMBeanAttributeInfo[]) info.getAttributes(); + OpenType type = null; + for (int a = 0; a < attribs.length; ++a) + if (attribs[a].getName().equals("attribute")) + type = attribs[a].getOpenType(); + if (value instanceof Map) + { + TabularType ttype = (TabularType) type; + TabularData data = new TabularDataSupport(ttype); + Iterator it = ((Map) value).entrySet().iterator(); + while (it.hasNext()) + { + Map.Entry entry = (Map.Entry) it.next(); + try + { + data.put(new CompositeDataSupport(ttype.getRowType(), + new String[] { + "key", + "value" + }, + new Object[] { + entry.getKey(), + entry.getValue() + })); + } + catch (OpenDataException e) + { + throw (InternalError) (new InternalError("A problem occurred " + + "converting the map " + + "to a composite data " + + "structure.").initCause(e)); + } + } + return data; + } + CompositeType cType = (CompositeType) type; + Set names = cType.keySet(); + Iterator it = names.iterator(); + List values = new ArrayList(names.size()); + while (it.hasNext()) + { + String field = (String) it.next(); + Method getter = null; + try + { + getter = vClass.getMethod("get" + field, null); + } + catch (NoSuchMethodException e) + { + /* Ignored; the type tells us it's there. */ + } + try + { + values.add(getter.invoke(value, null)); + } + catch (IllegalAccessException e) + { + throw new ReflectionException(e, "Failed to retrieve " + field); + } + catch (IllegalArgumentException e) + { + throw new ReflectionException(e, "Failed to retrieve " + field); + } + catch (InvocationTargetException e) + { + throw new MBeanException((Exception) e.getCause(), + "The getter of " + field + + " threw an exception"); + } + } + try + { + return new CompositeDataSupport(cType, + (String[]) + names.toArray(new String[names.size()]), + values.toArray()); + } + catch (OpenDataException e) + { + throw (InternalError) (new InternalError("A problem occurred " + + "converting the value " + + "to a composite data " + + "structure.").initCause(e)); + } + } + + protected MBeanInfo getCachedMBeanInfo() + { + return (MBeanInfo) openInfo; + } + + public MBeanInfo getMBeanInfo() + { + super.getMBeanInfo(); + return getCachedMBeanInfo(); + } + + private OpenType getTypeFromClass(Class c) + throws OpenDataException + { + return translate(c.getName()).getOpenType(); + } + + private OpenMBeanParameterInfo[] translateSignature(MBeanParameterInfo[] oldS) + throws OpenDataException + { + OpenMBeanParameterInfo[] sig = new OpenMBeanParameterInfoSupport[oldS.length]; + for (int a = 0; a < oldS.length; ++a) + { + OpenMBeanParameterInfo param = translate(oldS[a].getType()); + if (param.getMinValue() == null) + { + Object[] lv; + if (param.getLegalValues() == null) + lv = null; + else + lv = param.getLegalValues().toArray(); + sig[a] = new OpenMBeanParameterInfoSupport(oldS[a].getName(), + oldS[a].getDescription(), + param.getOpenType(), + param.getDefaultValue(), + lv); + } + else + sig[a] = new OpenMBeanParameterInfoSupport(oldS[a].getName(), + oldS[a].getDescription(), + param.getOpenType(), + param.getDefaultValue(), + param.getMinValue(), + param.getMaxValue()); + } + return sig; + } + + private OpenMBeanParameterInfo translate(String type) + throws OpenDataException + { + if (type.equals("boolean") || type.equals(Boolean.class.getName())) + return new OpenMBeanParameterInfoSupport("TransParam", + "Translated parameter", + SimpleType.BOOLEAN, + null, + new Object[] { + Boolean.TRUE, + Boolean.FALSE + }); + if (type.equals("byte") || type.equals(Byte.class.getName())) + return new OpenMBeanParameterInfoSupport("TransParam", + "Translated parameter", + SimpleType.BYTE, + null, + Byte.valueOf(Byte.MIN_VALUE), + Byte.valueOf(Byte.MAX_VALUE)); + if (type.equals("char") || type.equals(Character.class.getName())) + return new OpenMBeanParameterInfoSupport("TransParam", + "Translated parameter", + SimpleType.CHARACTER, + null, + Character.valueOf(Character.MIN_VALUE), + Character.valueOf(Character.MAX_VALUE)); + if (type.equals("double") || type.equals(Double.class.getName())) + return new OpenMBeanParameterInfoSupport("TransParam", + "Translated parameter", + SimpleType.DOUBLE, + null, + Double.valueOf(Double.MIN_VALUE), + Double.valueOf(Double.MAX_VALUE)); + if (type.equals("float") || type.equals(Float.class.getName())) + return new OpenMBeanParameterInfoSupport("TransParam", + "Translated parameter", + SimpleType.FLOAT, + null, + Float.valueOf(Float.MIN_VALUE), + Float.valueOf(Float.MAX_VALUE)); + if (type.equals("int") || type.equals(Integer.class.getName())) + return new OpenMBeanParameterInfoSupport("TransParam", + "Translated parameter", + SimpleType.INTEGER, + null, + Integer.valueOf(Integer.MIN_VALUE), + Integer.valueOf(Integer.MAX_VALUE)); + if (type.equals("long") || type.equals(Long.class.getName())) + return new OpenMBeanParameterInfoSupport("TransParam", + "Translated parameter", + SimpleType.LONG, + null, + Long.valueOf(Long.MIN_VALUE), + Long.valueOf(Long.MAX_VALUE)); + if (type.equals("short") || type.equals(Short.class.getName())) + return new OpenMBeanParameterInfoSupport("TransParam", + "Translated parameter", + SimpleType.SHORT, + null, + Short.valueOf(Short.MIN_VALUE), + Short.valueOf(Short.MAX_VALUE)); + if (type.equals(String.class.getName())) + return new OpenMBeanParameterInfoSupport("TransParam", + "Translated parameter", + SimpleType.STRING); + if (type.equals("void")) + return new OpenMBeanParameterInfoSupport("TransParam", + "Translated parameter", + SimpleType.VOID); + if (type.startsWith("java.util.Map")) + { + int lparam = type.indexOf("<"); + int comma = type.indexOf(",", lparam); + int rparam = type.indexOf(">", comma); + String key = type.substring(lparam + 1, comma).trim(); + OpenType k = translate(key).getOpenType(); + OpenType v = translate(type.substring(comma + 1, rparam).trim()).getOpenType(); + CompositeType ctype = new CompositeType(Map.class.getName(), Map.class.getName(), + new String[] { "key", "value" }, + new String[] { "Map key", "Map value"}, + new OpenType[] { k, v}); + TabularType ttype = new TabularType(key, key, ctype, + new String[] { "key" }); + return new OpenMBeanParameterInfoSupport("TransParam", + "Translated parameter", + ttype); + } + if (type.startsWith("java.util.List")) + { + int lparam = type.indexOf("<"); + int rparam = type.indexOf(">"); + OpenType e = translate(type.substring(lparam + 1, rparam).trim()).getOpenType(); + return new OpenMBeanParameterInfoSupport("TransParam", + "Translated parameter", + new ArrayType(1, e) + ); + } + Class c; + try + { + c = Class.forName(type); + } + catch (ClassNotFoundException e) + { + throw (InternalError) + (new InternalError("The class for a type used in a management bean " + + "could not be loaded.").initCause(e)); + } + if (c.isEnum()) + { + Object[] values = c.getEnumConstants(); + String[] names = new String[values.length]; + for (int a = 0; a < values.length; ++a) + names[a] = values[a].toString(); + return new OpenMBeanParameterInfoSupport("TransParam", + "Translated parameter", + SimpleType.STRING, + null, + (Object[]) names); + } + try + { + c.getMethod("from", new Class[] { CompositeData.class }); + Method[] methods = c.getMethods(); + List names = new ArrayList(); + List types = new ArrayList(); + for (int a = 0; a < methods.length; ++a) + { + String name = methods[a].getName(); + if (name.startsWith("get")) + { + names.add(name.substring(3)); + types.add(getTypeFromClass(methods[a].getReturnType())); + } + } + String[] fields = (String[]) names.toArray(); + CompositeType ctype = new CompositeType(c.getName(), c.getName(), + fields, fields, + (OpenType[]) types.toArray()); + return new OpenMBeanParameterInfoSupport("TransParam", + "Translated parameter", + ctype); + } + catch (NoSuchMethodException e) + { + /* Ignored; we expect this if this isn't a from(CompositeData) class */ + } + if (c.isArray()) + { + int depth; + for (depth = 0; c.getName().charAt(depth) == '['; ++depth); + OpenType ot = getTypeFromClass(c.getComponentType()); + return new OpenMBeanParameterInfoSupport("TransParam", + "Translated parameter", + new ArrayType(depth, ot) + ); + } + throw new InternalError("The type used does not have an open type translation."); + } + } diff --git a/libjava/classpath/gnu/java/lang/management/MemoryMXBeanImpl.java b/libjava/classpath/gnu/java/lang/management/MemoryMXBeanImpl.java index 064e19a1970..8cb10420213 100644 --- a/libjava/classpath/gnu/java/lang/management/MemoryMXBeanImpl.java +++ b/libjava/classpath/gnu/java/lang/management/MemoryMXBeanImpl.java @@ -37,6 +37,8 @@ exception statement from your version. */ package gnu.java.lang.management; +import gnu.classpath.ListenerData; + import java.lang.management.MemoryMXBean; import java.lang.management.MemoryNotificationInfo; import java.lang.management.MemoryUsage; @@ -168,49 +170,6 @@ public final class MemoryMXBeanImpl VMMemoryMXBeanImpl.setVerbose(verbose); } - private class ListenerData - { - private NotificationListener listener; - private NotificationFilter filter; - private Object passback; - - public ListenerData(NotificationListener listener, - NotificationFilter filter, Object passback) - { - this.listener = listener; - this.filter = filter; - this.passback = passback; - } - - public NotificationListener getListener() - { - return listener; - } - - public NotificationFilter getFilter() - { - return filter; - } - - public Object getPassback() - { - return passback; - } - - public boolean equals(Object obj) - { - if (obj instanceof ListenerData) - { - ListenerData data = (ListenerData) obj; - return (data.getListener() == listener && - data.getFilter() == filter && - data.getPassback() == passback); - } - return false; - } - - } - public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object passback) diff --git a/libjava/classpath/gnu/java/lang/management/MemoryPoolMXBeanImpl.java b/libjava/classpath/gnu/java/lang/management/MemoryPoolMXBeanImpl.java index ed4dccd769e..e314ca1648a 100644 --- a/libjava/classpath/gnu/java/lang/management/MemoryPoolMXBeanImpl.java +++ b/libjava/classpath/gnu/java/lang/management/MemoryPoolMXBeanImpl.java @@ -40,6 +40,7 @@ package gnu.java.lang.management; import gnu.classpath.SystemProperties; import java.lang.management.MemoryPoolMXBean; +import java.lang.management.MemoryType; import java.lang.management.MemoryUsage; import javax.management.NotCompliantMBeanException; @@ -133,9 +134,10 @@ public final class MemoryPoolMXBeanImpl return null; } - public String getType() + public MemoryType getType() { - return VMMemoryPoolMXBeanImpl.getType(name); + return + MemoryType.valueOf(VMMemoryPoolMXBeanImpl.getType(name)); } public MemoryUsage getUsage() diff --git a/libjava/classpath/gnu/java/lang/reflect/ClassSignatureParser.java b/libjava/classpath/gnu/java/lang/reflect/ClassSignatureParser.java index 0c9b962921e..31f28385f44 100644 --- a/libjava/classpath/gnu/java/lang/reflect/ClassSignatureParser.java +++ b/libjava/classpath/gnu/java/lang/reflect/ClassSignatureParser.java @@ -61,7 +61,7 @@ public class ClassSignatureParser extends GenericSignatureParser } // SuperclassSignature superclassType = readClassTypeSignature(); - ArrayList interfaces = new ArrayList(); + ArrayList<Type> interfaces = new ArrayList<Type>(); while (peekChar() == 'L') { // SuperinterfaceSignature diff --git a/libjava/classpath/gnu/java/lang/reflect/GenericSignatureParser.java b/libjava/classpath/gnu/java/lang/reflect/GenericSignatureParser.java index 399f1bdc46f..06bc9e4ab06 100644 --- a/libjava/classpath/gnu/java/lang/reflect/GenericSignatureParser.java +++ b/libjava/classpath/gnu/java/lang/reflect/GenericSignatureParser.java @@ -60,7 +60,6 @@ final class TypeVariableImpl extends TypeImpl implements TypeVariable return this; } - /* FIXME[GENERICS]: Remove cast */ public Type[] getBounds() { resolve(bounds); @@ -141,10 +140,9 @@ final class ParameterizedTypeImpl extends TypeImpl implements ParameterizedType return this; } - /* FIXME[GENERICS]: Remove cast */ public Type[] getActualTypeArguments() { - return (Type[]) typeArgs.clone(); + return (Type[]) typeArgs.clone(); } public Type getRawType() @@ -276,12 +274,11 @@ final class UnresolvedTypeVariable extends TypeImpl implements Type GenericDeclaration d = decl; while (d != null) { - TypeVariable[] vars = d.getTypeParameters(); - for (int a = 0; a < vars.length ; ++a) + for (TypeVariable t : d.getTypeParameters()) { - if (vars[a].getName().equals(name)) + if (t.getName().equals(name)) { - return vars[a]; + return t; } } d = getParent(d); @@ -414,7 +411,7 @@ class GenericSignatureParser TypeVariable[] readFormalTypeParameters() { consume('<'); - ArrayList params = new ArrayList(); + ArrayList<TypeVariable> params = new ArrayList<TypeVariable>(); do { // TODO should we handle name clashes? @@ -430,7 +427,7 @@ class GenericSignatureParser { String identifier = readIdentifier(); consume(':'); - ArrayList bounds = new ArrayList(); + ArrayList<Type> bounds = new ArrayList<Type>(); if (peekChar() != ':') { bounds.add(readFieldTypeSignature()); @@ -501,7 +498,7 @@ class GenericSignatureParser private Type[] readTypeArguments() { consume('<'); - ArrayList list = new ArrayList(); + ArrayList<Type> list = new ArrayList<Type>(); do { list.add(readTypeArgument()); diff --git a/libjava/classpath/gnu/java/lang/reflect/MethodSignatureParser.java b/libjava/classpath/gnu/java/lang/reflect/MethodSignatureParser.java index 16b1af99b73..50f98e29965 100644 --- a/libjava/classpath/gnu/java/lang/reflect/MethodSignatureParser.java +++ b/libjava/classpath/gnu/java/lang/reflect/MethodSignatureParser.java @@ -72,7 +72,7 @@ public class MethodSignatureParser extends GenericSignatureParser typeParameters = new TypeVariable[0]; } consume('('); - ArrayList args = new ArrayList(); + ArrayList<Type> args = new ArrayList<Type>(); while (peekChar() != ')') { args.add(readTypeSignature()); @@ -81,7 +81,7 @@ public class MethodSignatureParser extends GenericSignatureParser args.toArray(argTypes); consume(')'); retType = readTypeSignature(); - ArrayList throwsSigs = new ArrayList(); + ArrayList<Type> throwsSigs = new ArrayList<Type>(); while (peekChar() == '^') { consume('^'); diff --git a/libjava/classpath/gnu/java/locale/.cvsignore b/libjava/classpath/gnu/java/locale/.cvsignore new file mode 100644 index 00000000000..d41ae8d8147 --- /dev/null +++ b/libjava/classpath/gnu/java/locale/.cvsignore @@ -0,0 +1 @@ +LocaleData.java diff --git a/libjava/classpath/gnu/java/net/DefaultProxySelector.java b/libjava/classpath/gnu/java/net/DefaultProxySelector.java new file mode 100644 index 00000000000..31f861e8710 --- /dev/null +++ b/libjava/classpath/gnu/java/net/DefaultProxySelector.java @@ -0,0 +1,80 @@ +/* DefaultProxySelector.java -- + 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.net; + +import java.io.IOException; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.SocketAddress; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +public final class DefaultProxySelector + extends ProxySelector +{ + private static final List<Proxy> proxies = new ArrayList<Proxy>(); + + static + { + // The default proxy selector supports only direct connections. + proxies.add(Proxy.NO_PROXY); + } + + public DefaultProxySelector() + { + // Do nothing by default. + } + + public void connectFailed(URI uri, SocketAddress sa, IOException ioe) + { + if (uri == null || sa == null || ioe == null) + throw new IllegalArgumentException(); + + // Do nothing by default. + } + + public List<Proxy> select(URI uri) + { + if (uri == null) + throw new IllegalArgumentException(); + + return proxies; + } +} diff --git a/libjava/classpath/gnu/java/net/PlainDatagramSocketImpl.java b/libjava/classpath/gnu/java/net/PlainDatagramSocketImpl.java index 0fcd780dfb9..a84525e18b8 100644 --- a/libjava/classpath/gnu/java/net/PlainDatagramSocketImpl.java +++ b/libjava/classpath/gnu/java/net/PlainDatagramSocketImpl.java @@ -1,5 +1,5 @@ /* PlainDatagramSocketImpl.java -- Default DatagramSocket implementation - Copyright (C) 1998, 1999, 2001, 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2001, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,13 +38,19 @@ exception statement from your version. */ package gnu.java.net; +import gnu.java.nio.VMChannel; + import java.io.IOException; +import java.io.InterruptedIOException; import java.net.DatagramPacket; import java.net.DatagramSocketImpl; import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.NetworkInterface; import java.net.SocketAddress; import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.nio.ByteBuffer; /** * Written using on-line Java Platform 1.2 API Specification, as well @@ -62,11 +68,12 @@ import java.net.SocketException; */ public final class PlainDatagramSocketImpl extends DatagramSocketImpl { - + private final VMChannel channel; + /** - * This is the actual underlying file descriptor + * The platform-specific socket implementation. */ - int native_fd = -1; + private final VMPlainSocketImpl impl; /** * Lock object to serialize threads wanting to receive @@ -81,25 +88,26 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl /** * Default do nothing constructor */ - public PlainDatagramSocketImpl() + public PlainDatagramSocketImpl() throws IOException { - // Nothing to do here. + channel = new VMChannel(); + impl = new VMPlainSocketImpl(channel); } - protected void finalize() throws Throwable + /*protected void finalize() throws Throwable { synchronized (this) { - if (native_fd != -1) + if (channel.getState().isValid()) close(); } super.finalize(); - } + }*/ - public int getNativeFD() + /*public int getNativeFD() { return native_fd; - } + }*/ /** * Binds this socket to a particular port and interface @@ -109,20 +117,46 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl * * @exception SocketException If an error occurs */ - protected synchronized void bind(int port, InetAddress addr) + protected synchronized void bind(int port, InetAddress addr) throws SocketException - { - VMPlainDatagramSocketImpl.bind(this, port, addr); - } + { + try + { + impl.bind(new InetSocketAddress(addr, port)); + } + catch (SocketException se) + { + throw se; + } + catch (IOException ioe) + { + SocketException se = new SocketException(); + se.initCause(ioe); + throw se; + } + } /** * Creates a new datagram socket * * @exception SocketException If an error occurs */ - protected synchronized void create() throws SocketException + protected synchronized void create() throws SocketException { - VMPlainDatagramSocketImpl.create(this); + try + { + channel.initSocket(false); + } + catch (SocketException se) + { + throw se; + } + catch (IOException ioe) + { + SocketException se = new SocketException(); + se.initCause(ioe); + throw se; + } } /** @@ -135,7 +169,7 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl */ protected void connect(InetAddress addr, int port) throws SocketException { - VMPlainDatagramSocketImpl.connect(this, addr, port); + channel.connect(new InetSocketAddress(addr, port), 0); } /** @@ -147,8 +181,14 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl { synchronized (this) { - if (native_fd != -1) - close(); + try + { + if (channel.getState().isValid()) + channel.disconnect(); + } + catch (IOException ioe) + { + } } } @@ -161,7 +201,7 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl */ protected synchronized void setTimeToLive(int ttl) throws IOException { - setOption(VMPlainDatagramSocketImpl.IP_TTL, new Integer(ttl)); + impl.setTimeToLive(ttl); } /** @@ -173,15 +213,27 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl */ protected synchronized int getTimeToLive() throws IOException { - Object obj = getOption(VMPlainDatagramSocketImpl.IP_TTL); + return impl.getTimeToLive(); + } - if (! (obj instanceof Integer)) - throw new IOException("Internal Error"); + protected int getLocalPort() + { + if (channel == null) + return -1; - return ((Integer) obj).intValue(); + try + { + InetSocketAddress local = channel.getLocalAddress(); + if (local == null) + return -1; + return local.getPort(); + } + catch (IOException ioe) + { + return -1; + } } - /** * Sends a packet of data to a remote host * @@ -191,13 +243,30 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl */ protected void send(DatagramPacket packet) throws IOException { - if (native_fd != -1) + synchronized (SEND_LOCK) { - synchronized(SEND_LOCK) + ByteBuffer buf = ByteBuffer.wrap(packet.getData(), + packet.getOffset(), + packet.getLength()); + InetAddress remote = packet.getAddress(); + int port = packet.getPort(); + if (remote == null) + throw new NullPointerException(); + if (port <= 0) + throw new SocketException("invalid port " + port); + while (true) { - VMPlainDatagramSocketImpl.send(this, packet); + try + { + channel.send(buf, new InetSocketAddress(remote, port)); + break; + } + catch (InterruptedIOException ioe) + { + // Ignore; interrupted system call. + } } - } + } } /** @@ -210,48 +279,123 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl protected void receive(DatagramPacket packet) throws IOException { - synchronized(RECEIVE_LOCK) - { - VMPlainDatagramSocketImpl.receive(this, packet); - } + synchronized(RECEIVE_LOCK) + { + ByteBuffer buf = ByteBuffer.wrap(packet.getData(), + packet.getOffset(), + packet.getLength()); + SocketAddress addr = null; + while (true) + { + try + { + addr = channel.receive(buf); + break; + } + catch (SocketTimeoutException ste) + { + throw ste; + } + catch (InterruptedIOException iioe) + { + // Ignore. Loop. + } + } + if (addr != null) + packet.setSocketAddress(addr); + packet.setLength(buf.position() - packet.getOffset()); + } } /** * Sets the value of an option on the socket * - * @param option_id The identifier of the option to set - * @param val The value of the option to set + * @param optionId The identifier of the option to set + * @param value The value of the option to set * * @exception SocketException If an error occurs */ - public synchronized void setOption(int option_id, Object val) + public synchronized void setOption(int optionId, Object value) throws SocketException - { - VMPlainDatagramSocketImpl.setOption(this, option_id, val); - } + { + switch (optionId) + { + case IP_MULTICAST_IF: + case IP_MULTICAST_IF2: + impl.setMulticastInterface(optionId, (InetAddress) value); + break; + + case IP_MULTICAST_LOOP: + case SO_BROADCAST: + case SO_KEEPALIVE: + case SO_OOBINLINE: + case TCP_NODELAY: + case IP_TOS: + case SO_LINGER: + case SO_RCVBUF: + case SO_SNDBUF: + case SO_TIMEOUT: + case SO_REUSEADDR: + impl.setOption(optionId, value); + return; + + default: + throw new SocketException("cannot set option " + optionId); + } + } /** * Retrieves the value of an option on the socket * - * @param option_id The identifier of the option to retrieve + * @param optionId The identifier of the option to retrieve * * @return The value of the option * * @exception SocketException If an error occurs */ - public synchronized Object getOption(int option_id) + public synchronized Object getOption(int optionId) throws SocketException - { - return VMPlainDatagramSocketImpl.getOption(this, option_id); - } + { + if (optionId == SO_BINDADDR) + { + try + { + InetSocketAddress local = channel.getLocalAddress(); + if (local == null) + return null; + return local.getAddress(); + } + catch (SocketException se) + { + throw se; + } + catch (IOException ioe) + { + SocketException se = new SocketException(); + se.initCause(ioe); + throw se; + } + } + if (optionId == IP_MULTICAST_IF || optionId == IP_MULTICAST_IF2) + return impl.getMulticastInterface(optionId); + + return impl.getOption(optionId); + } /** * Closes the socket */ protected synchronized void close() { - VMPlainDatagramSocketImpl.close(this); + try + { + if (channel.getState().isValid()) + channel.close(); + } + catch (IOException ioe) + { + } } /** @@ -291,7 +435,7 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl */ protected synchronized void join(InetAddress addr) throws IOException { - VMPlainDatagramSocketImpl.join(this,addr); + impl.join(addr); } /** @@ -303,7 +447,7 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl */ protected synchronized void leave(InetAddress addr) throws IOException { - VMPlainDatagramSocketImpl.leave(this, addr); + impl.leave(addr); } /** @@ -323,12 +467,20 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl public void joinGroup(SocketAddress address, NetworkInterface netIf) throws IOException { - VMPlainDatagramSocketImpl.joinGroup(this, address, netIf); + if (address == null) + throw new NullPointerException(); + if (!(address instanceof InetSocketAddress)) + throw new SocketException("unknown address type"); + impl.joinGroup((InetSocketAddress) address, netIf); } public void leaveGroup(SocketAddress address, NetworkInterface netIf) throws IOException { - VMPlainDatagramSocketImpl.leaveGroup(this, address, netIf); + if (address == null) + throw new NullPointerException(); + if (!(address instanceof InetSocketAddress)) + throw new SocketException("unknown address type"); + impl.leaveGroup((InetSocketAddress) address, netIf); } } diff --git a/libjava/classpath/gnu/java/net/PlainSocketImpl.java b/libjava/classpath/gnu/java/net/PlainSocketImpl.java index 47d05aa410d..5bda0a5e9bf 100644 --- a/libjava/classpath/gnu/java/net/PlainSocketImpl.java +++ b/libjava/classpath/gnu/java/net/PlainSocketImpl.java @@ -39,13 +39,20 @@ exception statement from your version. */ package gnu.java.net; +import gnu.java.nio.SocketChannelImpl; +import gnu.java.nio.VMChannel; + import java.io.InputStream; import java.io.IOException; +import java.io.InterruptedIOException; import java.io.OutputStream; import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.SocketException; import java.net.SocketImpl; +import java.net.SocketTimeoutException; +import java.nio.ByteBuffer; /** * Written using on-line Java Platform 1.2 API Specification, as well @@ -63,17 +70,13 @@ import java.net.SocketImpl; * @author Nic Ferrier (nferrier@tapsellferrier.co.uk) * @author Aaron M. Renn (arenn@urbanophile.com) */ -public final class PlainSocketImpl extends SocketImpl +public class PlainSocketImpl extends SocketImpl { /** - * The OS file handle representing the socket. - * This is used for reads and writes to/from the socket and - * to close it. - * - * When the socket is closed this is reset to -1. + * The underlying plain socket VM implementation. */ - int native_fd = -1; + protected VMPlainSocketImpl impl; /** * A cached copy of the in stream for reading from the socket. @@ -90,7 +93,13 @@ public final class PlainSocketImpl extends SocketImpl * is being invoked on this socket. */ private boolean inChannelOperation; - + + /** + * The socket channel we use for IO operation. Package-private for + * use by inner classes. + */ + SocketChannelImpl channel; + /** * Indicates whether we should ignore whether any associated * channel is set to non-blocking mode. Certain operations @@ -117,29 +126,7 @@ public final class PlainSocketImpl extends SocketImpl */ public PlainSocketImpl() { - // Nothing to do here. - } - - protected void finalize() throws Throwable - { - synchronized (this) - { - if (native_fd != -1) - try - { - close(); - } - catch (IOException ex) - { - // Nothing we can do about it. - } - } - super.finalize(); - } - - public int getNativeFD() - { - return native_fd; + this.impl = new VMPlainSocketImpl(); } /** @@ -155,7 +142,24 @@ public final class PlainSocketImpl extends SocketImpl */ public void setOption(int optionId, Object value) throws SocketException { - VMPlainSocketImpl.setOption(this, optionId, value); + switch (optionId) + { + case SO_LINGER: + case IP_MULTICAST_LOOP: + case SO_BROADCAST: + case SO_KEEPALIVE: + case SO_OOBINLINE: + case TCP_NODELAY: + case IP_TOS: + case SO_RCVBUF: + case SO_SNDBUF: + case SO_TIMEOUT: + case SO_REUSEADDR: + impl.setOption(optionId, value); + return; + default: + throw new SocketException("Unrecognized TCP option: " + optionId); + } } /** @@ -171,17 +175,49 @@ public final class PlainSocketImpl extends SocketImpl */ public Object getOption(int optionId) throws SocketException { - return VMPlainSocketImpl.getOption(this, optionId); + if (optionId == SO_BINDADDR) + { + try + { + return channel.getVMChannel().getLocalAddress().getAddress(); + } + catch (IOException ioe) + { + SocketException se = new SocketException(); + se.initCause(ioe); + throw se; + } + } + + // This filters options which are invalid for TCP. + switch (optionId) + { + case SO_LINGER: + case IP_MULTICAST_LOOP: + case SO_BROADCAST: + case SO_KEEPALIVE: + case SO_OOBINLINE: + case TCP_NODELAY: + case IP_TOS: + case SO_RCVBUF: + case SO_SNDBUF: + case SO_TIMEOUT: + case SO_REUSEADDR: + return impl.getOption(optionId); + default: + throw new SocketException("Unrecognized TCP option: " + optionId); + } + } public void shutdownInput() throws IOException { - VMPlainSocketImpl.shutdownInput(this); + impl.shutdownInput(); } public void shutdownOutput() throws IOException { - VMPlainSocketImpl.shutdownOutput(this); + impl.shutdownOutput(); } /** @@ -195,7 +231,11 @@ public final class PlainSocketImpl extends SocketImpl */ protected synchronized void create(boolean stream) throws IOException { - VMPlainSocketImpl.create(this); + channel = new SocketChannelImpl(false); + VMChannel vmchannel = channel.getVMChannel(); + vmchannel.initSocket(stream); + channel.configureBlocking(true); + impl.getState().setChannelFD(vmchannel.getState()); } /** @@ -222,7 +262,7 @@ public final class PlainSocketImpl extends SocketImpl */ protected void connect(InetAddress addr, int port) throws IOException { - VMPlainSocketImpl.connect(this, addr, port); + connect(new InetSocketAddress(addr, port), 0); } /** @@ -236,7 +276,17 @@ public final class PlainSocketImpl extends SocketImpl protected synchronized void connect(SocketAddress address, int timeout) throws IOException { - VMPlainSocketImpl.connect(this, address, timeout); + if (channel == null) + create(true); + boolean connected = channel.connect(address, timeout); + if (!connected) + throw new SocketTimeoutException("connect timed out"); + + // Using the given SocketAddress is important to preserve + // hostnames given by the caller. + InetSocketAddress addr = (InetSocketAddress) address; + this.address = addr.getAddress(); + this.port = addr.getPort(); } /** @@ -251,7 +301,10 @@ public final class PlainSocketImpl extends SocketImpl protected synchronized void bind(InetAddress addr, int port) throws IOException { - VMPlainSocketImpl.bind(this, addr, port); + if (channel == null) + create(true); + impl.bind(new InetSocketAddress(addr, port)); + localport = channel.getVMChannel().getLocalAddress().getPort(); } /** @@ -267,7 +320,7 @@ public final class PlainSocketImpl extends SocketImpl protected synchronized void listen(int queuelen) throws IOException { - VMPlainSocketImpl.listen(this, queuelen); + impl.listen(queuelen); } /** @@ -279,7 +332,19 @@ public final class PlainSocketImpl extends SocketImpl protected synchronized void accept(SocketImpl impl) throws IOException { - VMPlainSocketImpl.accept(this, impl); + if (channel == null) + create(true); + if (!(impl instanceof PlainSocketImpl)) + throw new IOException("incompatible SocketImpl: " + + impl.getClass().getName()); + PlainSocketImpl that = (PlainSocketImpl) impl; + VMChannel c = channel.getVMChannel().accept(); + that.impl.getState().setChannelFD(c.getState()); + that.channel = new SocketChannelImpl(c); + that.setOption(SO_REUSEADDR, Boolean.TRUE); + // Reset the inherited timeout. + that.setOption(SO_TIMEOUT, Integer.valueOf(0)); + } /** @@ -292,7 +357,9 @@ public final class PlainSocketImpl extends SocketImpl */ protected int available() throws IOException { - return VMPlainSocketImpl.available(this); + if (channel == null) + throw new SocketException("not connected"); + return channel.getVMChannel().available(); } /** @@ -308,65 +375,16 @@ public final class PlainSocketImpl extends SocketImpl */ protected void close() throws IOException { - VMPlainSocketImpl.close(this); - } - - public void sendUrgentData(int data) - { - VMPlainSocketImpl.sendUrgendData(this, data); - } - - /** - * Internal method used by SocketInputStream for reading data from - * the connection. Reads up to len bytes of data into the buffer - * buf starting at offset bytes into the buffer. - * - * @return the actual number of bytes read or -1 if end of stream. - * - * @throws IOException if an error occurs - */ - protected int read(byte[] buf, int offset, int len) - throws IOException - { - return VMPlainSocketImpl.read(this, buf, offset, len); - } - - /** - * Internal method used by SocketInputStream for reading data from - * the connection. Reads and returns one byte of data. - * - * @return the read byte - * - * @throws IOException if an error occurs - */ - protected int read() - throws IOException - { - return VMPlainSocketImpl.read(this); - } - - /** - * Internal method used by SocketOuputStream for writing data to - * the connection. Writes up to len bytes of data from the buffer - * buf starting at offset bytes into the buffer. - * - * @throws IOException If an error occurs - */ - protected void write(byte[] buf, int offset, int len) - throws IOException - { - VMPlainSocketImpl.write(this, buf, offset, len); + if (impl.getState().isValid()) + impl.close(); + + address = null; + port = -1; } - /** - * Internal method used by SocketOuputStream for writing data to - * the connection. Writes up one byte to the socket. - * - * @throws IOException If an error occurs - */ - protected void write(int data) throws IOException + public void sendUrgentData(int data) throws IOException { - VMPlainSocketImpl.write(this, data); + impl.sendUrgentData(data); } /** @@ -400,6 +418,95 @@ public final class PlainSocketImpl extends SocketImpl return out; } + + public VMChannel getVMChannel() + { + if (channel == null) + return null; + return channel.getVMChannel(); + } + + /* (non-Javadoc) + * @see java.net.SocketImpl#getInetAddress() + */ + protected InetAddress getInetAddress() + { + if (channel == null) + return null; + + try + { + InetSocketAddress remote = channel.getVMChannel().getPeerAddress(); + if (remote == null) + return null; + // To mimic behavior of the RI the InetAddress instance which was + // used to establish the connection is returned instead of one that + // was created by the native layer (this preserves exact hostnames). + if (address != null) + return address; + + return remote.getAddress(); + } + catch (IOException ioe) + { + return null; + } + } + + /* (non-Javadoc) + * @see java.net.SocketImpl#getLocalPort() + */ + protected int getLocalPort() + { + if (channel == null) + return -1; + try + { + InetSocketAddress local = channel.getVMChannel().getLocalAddress(); + if (local == null) + return -1; + return local.getPort(); + } + catch (IOException ioe) + { + return -1; + } + } + + public InetSocketAddress getLocalAddress() + { + if (channel == null) + return null; + try + { + return channel.getVMChannel().getLocalAddress(); + } + catch (IOException ioe) + { + return null; + } + } + + /* (non-Javadoc) + * @see java.net.SocketImpl#getPort() + */ + protected int getPort() + { + if (channel == null) + return -1; + + try + { + InetSocketAddress remote = channel.getVMChannel().getPeerAddress(); + if (remote == null) + return -1; + return remote.getPort(); + } + catch (IOException ioe) + { + return -1; + } + } /** * This class contains an implementation of <code>InputStream</code> for @@ -437,7 +544,23 @@ public final class PlainSocketImpl extends SocketImpl */ public int read() throws IOException { - return PlainSocketImpl.this.read(); + if (channel == null) + throw new SocketException("not connected"); + while (true) + { + try + { + return channel.getVMChannel().read(); + } + catch (SocketTimeoutException ste) + { + throw ste; + } + catch (InterruptedIOException iioe) + { + // Ignore; NIO may throw this; net io shouldn't + } + } } /** @@ -454,12 +577,24 @@ public final class PlainSocketImpl extends SocketImpl */ public int read (byte[] buf, int offset, int len) throws IOException { - int bytes_read = PlainSocketImpl.this.read (buf, offset, len); - - if (bytes_read == 0) - return -1; - - return bytes_read; + if (channel == null) + throw new SocketException("not connected"); + ByteBuffer b = ByteBuffer.wrap(buf, offset, len); + while (true) + { + try + { + return channel.read(b); + } + catch (SocketTimeoutException ste) + { + throw ste; + } + catch (InterruptedIOException iioe) + { + // Ignored; NIO may throw this; net IO not. + } + } } } @@ -495,7 +630,20 @@ public final class PlainSocketImpl extends SocketImpl */ public void write(int b) throws IOException { - PlainSocketImpl.this.write(b); + if (channel == null) + throw new SocketException("not connected"); + while (true) + { + try + { + channel.getVMChannel().write(b); + return; + } + catch (InterruptedIOException iioe) + { + // Ignored. + } + } } /** @@ -510,7 +658,21 @@ public final class PlainSocketImpl extends SocketImpl */ public void write (byte[] buf, int offset, int len) throws IOException { - PlainSocketImpl.this.write (buf, offset, len); + if (channel == null) + throw new SocketException("not connected"); + ByteBuffer b = ByteBuffer.wrap(buf, offset, len); + while (b.hasRemaining()) + { + try + { + if (channel.write(b) == -1) + throw new IOException("channel has been closed"); + } + catch (InterruptedIOException iioe) + { + // Ignored. + } + } } } } diff --git a/libjava/classpath/gnu/java/net/local/LocalSocketImpl.java b/libjava/classpath/gnu/java/net/local/LocalSocketImpl.java index f43305a8027..f49b79947b7 100644 --- a/libjava/classpath/gnu/java/net/local/LocalSocketImpl.java +++ b/libjava/classpath/gnu/java/net/local/LocalSocketImpl.java @@ -38,6 +38,8 @@ exception statement from your version. */ package gnu.java.net.local; +import gnu.classpath.Configuration; + import java.io.FileDescriptor; import java.io.InputStream; import java.io.IOException; @@ -66,7 +68,10 @@ final class LocalSocketImpl extends SocketImpl { try { - System.loadLibrary ("javanet"); + if (Configuration.INIT_LOAD_LIBRARY) + { + System.loadLibrary ("javanet"); + } } catch (Exception x) { diff --git a/libjava/classpath/gnu/java/net/protocol/http/HTTPConnection.java b/libjava/classpath/gnu/java/net/protocol/http/HTTPConnection.java index f5e831c6a01..3956d2aba3a 100644 --- a/libjava/classpath/gnu/java/net/protocol/http/HTTPConnection.java +++ b/libjava/classpath/gnu/java/net/protocol/http/HTTPConnection.java @@ -48,6 +48,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; +import java.net.SocketException; import java.security.GeneralSecurityException; import java.util.ArrayList; import java.util.HashMap; @@ -227,10 +228,16 @@ public class HTTPConnection * @param secure whether to use a secure connection * @param connectionTimeout the connection timeout * @param timeout the socket read timeout + * + * @throws IllegalArgumentException if either connectionTimeout or + * timeout less than zero. */ public HTTPConnection(String hostname, int port, boolean secure, int connectionTimeout, int timeout) { + if (connectionTimeout < 0 || timeout < 0) + throw new IllegalArgumentException(); + this.hostname = hostname; this.port = port; this.secure = secure; @@ -471,14 +478,32 @@ public class HTTPConnection { String ttl = SystemProperties.getProperty("classpath.net.http.keepAliveTTL"); - connectionTTL = (ttl != null && ttl.length() > 0) ? - 1000 * Math.max(1, Integer.parseInt(ttl)) : 10000; + connectionTTL = 10000; + if (ttl != null && ttl.length() > 0) + try + { + int v = 1000 * Integer.parseInt(ttl); + if (v >= 0) + connectionTTL = v; + } + catch (NumberFormatException _) + { + // Ignore. + } String mc = SystemProperties.getProperty("http.maxConnections"); - maxConnections = (mc != null && mc.length() > 0) ? - Math.max(Integer.parseInt(mc), 1) : 5; - if (maxConnections < 1) - maxConnections = 1; + maxConnections = 5; + if (mc != null && mc.length() > 0) + try + { + int v = Integer.parseInt(mc); + if (v > 0) + maxConnections = v; + } + catch (NumberFormatException _) + { + // Ignore. + } HTTPConnection c = null; @@ -490,12 +515,23 @@ public class HTTPConnection { c = cc; it.remove(); + // Update the timeout. + if (c.socket != null) + try + { + c.socket.setSoTimeout(timeout); + } + catch (SocketException _) + { + // Ignore. + } break; } } if (c == null) { - c = new HTTPConnection(host, port, secure, connectionTimeout, timeout); + c = new HTTPConnection(host, port, secure, + connectionTimeout, timeout); c.setPool(this); } return c; diff --git a/libjava/classpath/gnu/java/net/protocol/http/HTTPURLConnection.java b/libjava/classpath/gnu/java/net/protocol/http/HTTPURLConnection.java index cc68a3bac30..6c926b72fb4 100644 --- a/libjava/classpath/gnu/java/net/protocol/http/HTTPURLConnection.java +++ b/libjava/classpath/gnu/java/net/protocol/http/HTTPURLConnection.java @@ -75,7 +75,7 @@ public class HTTPURLConnection // These are package private for use in anonymous inner classes. String proxyHostname; - int proxyPort; + int proxyPort = -1; String agent; boolean keepAlive; @@ -99,18 +99,21 @@ public class HTTPURLConnection { super(url); requestHeaders = new Headers(); - proxyHostname = SystemProperties.getProperty("http.proxyHost"); - if (proxyHostname != null && proxyHostname.length() > 0) + String proxy = SystemProperties.getProperty("http.proxyHost"); + if (proxy != null && proxy.length() > 0) { String port = SystemProperties.getProperty("http.proxyPort"); if (port != null && port.length() > 0) { - proxyPort = Integer.parseInt(port); - } - else - { - proxyHostname = null; - proxyPort = -1; + try + { + proxyPort = Integer.parseInt(port); + proxyHostname = proxy; + } + catch (NumberFormatException _) + { + // Ignore. + } } } agent = SystemProperties.getProperty("http.agent"); @@ -354,11 +357,14 @@ public class HTTPURLConnection HTTPConnection connection; if (keepAlive) { - connection = HTTPConnection.Pool.instance.get(host, port, secure, getConnectTimeout(), 0); + connection = HTTPConnection.Pool.instance.get(host, port, secure, + getConnectTimeout(), + getReadTimeout()); } else { - connection = new HTTPConnection(host, port, secure, 0, getConnectTimeout()); + connection = new HTTPConnection(host, port, secure, + getConnectTimeout(), getReadTimeout()); } return connection; } @@ -662,23 +668,23 @@ public class HTTPURLConnection } /** - * Set the connection timeout speed, in milliseconds, or zero if the timeout + * Set the read timeout, in milliseconds, or zero if the timeout * is to be considered infinite. * * Overloaded. * */ - public void setConnectTimeout(int timeout) + public void setReadTimeout(int timeout) throws IllegalArgumentException { - super.setConnectTimeout( timeout ); - if( connection == null ) + super.setReadTimeout(timeout); + if (connection == null) return; try { - connection.getSocket().setSoTimeout( timeout ); + connection.getSocket().setSoTimeout(timeout); } - catch(IOException se) + catch (IOException se) { // Ignore socket exceptions. } diff --git a/libjava/classpath/gnu/java/net/protocol/jar/Handler.java b/libjava/classpath/gnu/java/net/protocol/jar/Handler.java index 316d8cb02be..7c09766a557 100644 --- a/libjava/classpath/gnu/java/net/protocol/jar/Handler.java +++ b/libjava/classpath/gnu/java/net/protocol/jar/Handler.java @@ -1,5 +1,5 @@ /* gnu.java.net.protocol.jar.Handler - jar protocol handler for java.net - Copyright (C) 1999, 2002, 2003, 2005 Free Software Foundation, Inc. + Copyright (C) 1999, 2002, 2003, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -45,6 +45,9 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.StringTokenizer; /** * @author Kresten Krab Thorup (krab@gnu.org) @@ -114,7 +117,7 @@ public class Handler extends URLStreamHandler file = file.substring(0, idx + 1) + url_string; } - setURL (url, "jar", url.getHost(), url.getPort(), file, null); + setURL (url, "jar", url.getHost(), url.getPort(), flat(file), null); return; } @@ -149,6 +152,45 @@ public class Handler extends URLStreamHandler } /** + * Makes the given jar url string 'flat' by removing any . and .. from + * jar file path because ZipFile entries can only handle flat paths. + * Inside jar files '/' is always the path separator. + */ + private static String flat(String url_string) + { + int jar_stop = url_string.indexOf("!/"); + String jar_path = url_string.substring(jar_stop + 1, url_string.length()); + + if (jar_path.indexOf("/.") < 0) + return url_string; + + ArrayList tokens = new ArrayList(); + StringTokenizer st = new StringTokenizer(jar_path, "/"); + while (st.hasMoreTokens()) + { + String token = st.nextToken(); + if (token.equals(".")) + continue; + else if (token.equals("..")) + { + if (! tokens.isEmpty()) + tokens.remove(tokens.size() - 1); + } + else + tokens.add(token); + } + + StringBuffer path = new StringBuffer(url_string.length()); + path.append(url_string.substring(0, jar_stop + 1)); + + Iterator it = tokens.iterator(); + while (it.hasNext()) + path.append('/').append(it.next()); + + return path.toString(); + } + + /** * This method converts a Jar URL object into a String. * * @param url The URL object to convert diff --git a/libjava/classpath/gnu/java/nio/DatagramChannelImpl.java b/libjava/classpath/gnu/java/nio/DatagramChannelImpl.java index 4687bf3f59e..268ee0a8aa7 100644 --- a/libjava/classpath/gnu/java/nio/DatagramChannelImpl.java +++ b/libjava/classpath/gnu/java/nio/DatagramChannelImpl.java @@ -55,8 +55,10 @@ import java.nio.channels.spi.SelectorProvider; * @author Michael Koch */ public final class DatagramChannelImpl extends DatagramChannel + implements VMChannelOwner { private NIODatagramSocket socket; + private VMChannel channel; /** * Indicates whether this channel initiated whatever operation @@ -64,6 +66,16 @@ public final class DatagramChannelImpl extends DatagramChannel */ private boolean inChannelOperation; + protected DatagramChannelImpl (SelectorProvider provider) + throws IOException + { + super (provider); + socket = new NIODatagramSocket (new PlainDatagramSocketImpl(), this); + channel = new VMChannel(); + channel.initSocket(false); + configureBlocking(true); + } + /** * Indicates whether our datagram socket should ignore whether * we are set to non-blocking mode. Certain operations on our @@ -85,14 +97,6 @@ public final class DatagramChannelImpl extends DatagramChannel inChannelOperation = b; } - protected DatagramChannelImpl (SelectorProvider provider) - throws IOException - { - super (provider); - socket = new NIODatagramSocket (new PlainDatagramSocketImpl(), this); - configureBlocking(true); - } - public DatagramSocket socket () { return socket; @@ -101,13 +105,13 @@ public final class DatagramChannelImpl extends DatagramChannel protected void implCloseSelectableChannel () throws IOException { - socket.close (); + channel.close(); } protected void implConfigureBlocking (boolean blocking) throws IOException { - socket.setSoTimeout (blocking ? 0 : NIOConstants.DEFAULT_TIMEOUT); + channel.setBlocking(blocking); } public DatagramChannel connect (SocketAddress remote) @@ -116,20 +120,34 @@ public final class DatagramChannelImpl extends DatagramChannel if (!isOpen()) throw new ClosedChannelException(); - socket.connect (remote); + try + { + channel.connect((InetSocketAddress) remote, 0); + } + catch (ClassCastException cce) + { + throw new IOException("unsupported socked address type"); + } return this; } public DatagramChannel disconnect () throws IOException { - socket.disconnect (); + channel.disconnect(); return this; } - public boolean isConnected () + public boolean isConnected() { - return socket.isConnected (); + try + { + return channel.getPeerAddress() != null; + } + catch (IOException ioe) + { + return false; + } } public int write (ByteBuffer src) @@ -138,7 +156,7 @@ public final class DatagramChannelImpl extends DatagramChannel if (!isConnected ()) throw new NotYetConnectedException (); - return send (src, socket.getRemoteSocketAddress()); + return channel.write(src); } public long write (ByteBuffer[] srcs, int offset, int length) @@ -152,13 +170,11 @@ public final class DatagramChannelImpl extends DatagramChannel || (length < 0) || (length > (srcs.length - offset))) throw new IndexOutOfBoundsException(); - - long result = 0; - - for (int index = offset; index < offset + length; index++) - result += write (srcs [index]); - return result; + /* We are connected, meaning we will write these bytes to + * the host we connected to, so we don't need to explicitly + * give the host. */ + return channel.writeGathering(srcs, offset, length); } public int read (ByteBuffer dst) @@ -167,9 +183,7 @@ public final class DatagramChannelImpl extends DatagramChannel if (!isConnected ()) throw new NotYetConnectedException (); - int remaining = dst.remaining(); - receive (dst); - return remaining - dst.remaining(); + return channel.read(dst); } public long read (ByteBuffer[] dsts, int offset, int length) @@ -184,12 +198,8 @@ public final class DatagramChannelImpl extends DatagramChannel || (length > (dsts.length - offset))) throw new IndexOutOfBoundsException(); - long result = 0; - - for (int index = offset; index < offset + length; index++) - result += read (dsts [index]); - - return result; + /* Likewise, see the comment int write above. */ + return channel.readScattering(dsts, offset, length); } public SocketAddress receive (ByteBuffer dst) @@ -200,49 +210,12 @@ public final class DatagramChannelImpl extends DatagramChannel try { - DatagramPacket packet; - int len = dst.remaining(); - - if (dst.hasArray()) - { - packet = new DatagramPacket (dst.array(), - dst.arrayOffset() + dst.position(), - len); - } - else - { - packet = new DatagramPacket (new byte [len], len); - } - - boolean completed = false; - - try - { - begin(); - setInChannelOperation(true); - socket.receive (packet); - completed = true; - } - finally - { - end (completed); - setInChannelOperation(false); - } - - if (!dst.hasArray()) - { - dst.put (packet.getData(), packet.getOffset(), packet.getLength()); - } - else - { - dst.position (dst.position() + packet.getLength()); - } - - return packet.getSocketAddress(); + begin(); + return channel.receive(dst); } - catch (SocketTimeoutException e) + finally { - return null; + end(true); } } @@ -252,46 +225,18 @@ public final class DatagramChannelImpl extends DatagramChannel if (!isOpen()) throw new ClosedChannelException(); - if (target instanceof InetSocketAddress - && ((InetSocketAddress) target).isUnresolved()) - throw new IOException("Target address not resolved"); - - byte[] buffer; - int offset = 0; - int len = src.remaining(); + if (!(target instanceof InetSocketAddress)) + throw new IOException("can only send to inet socket addresses"); - if (src.hasArray()) - { - buffer = src.array(); - offset = src.arrayOffset() + src.position(); - } - else - { - buffer = new byte [len]; - src.get (buffer); - } - - DatagramPacket packet = new DatagramPacket (buffer, offset, len, target); - - boolean completed = false; - try - { - begin(); - setInChannelOperation(true); - socket.send(packet); - completed = true; - } - finally - { - end (completed); - setInChannelOperation(false); - } - - if (src.hasArray()) - { - src.position (src.position() + len); - } + InetSocketAddress dst = (InetSocketAddress) target; + if (dst.isUnresolved()) + throw new IOException("Target address not resolved"); - return len; + return channel.send(src, dst); + } + + public VMChannel getVMChannel() + { + return channel; } } diff --git a/libjava/classpath/gnu/java/nio/DatagramChannelSelectionKey.java b/libjava/classpath/gnu/java/nio/DatagramChannelSelectionKey.java index 698e07e348f..f192e5002d4 100644 --- a/libjava/classpath/gnu/java/nio/DatagramChannelSelectionKey.java +++ b/libjava/classpath/gnu/java/nio/DatagramChannelSelectionKey.java @@ -38,6 +38,7 @@ exception statement from your version. */ package gnu.java.nio; +import java.io.IOException; import java.nio.channels.spi.AbstractSelectableChannel; /** @@ -52,10 +53,16 @@ public final class DatagramChannelSelectionKey super (channel, selector); } + // FIXME don't use file descriptor integers public int getNativeFD() { - NIODatagramSocket socket = - (NIODatagramSocket) ((DatagramChannelImpl) ch).socket(); - return socket.getPlainDatagramSocketImpl().getNativeFD(); + try + { + return ((DatagramChannelImpl) ch).getVMChannel().getState().getNativeFD(); + } + catch (IOException ioe) + { + throw new IllegalStateException(ioe); + } } } diff --git a/libjava/classpath/gnu/java/nio/EpollSelectionKeyImpl.java b/libjava/classpath/gnu/java/nio/EpollSelectionKeyImpl.java new file mode 100644 index 00000000000..11113f3975c --- /dev/null +++ b/libjava/classpath/gnu/java/nio/EpollSelectionKeyImpl.java @@ -0,0 +1,122 @@ +/* EpollSelectionKeyImpl.java -- selection key for the epoll selector. + 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.nio.channels.CancelledKeyException; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.spi.AbstractSelectionKey; + +/** + * @author Casey Marshall (csm@gnu.org) + */ +public class EpollSelectionKeyImpl extends AbstractSelectionKey +{ + final int fd; + private final EpollSelectorImpl selector; + private final SelectableChannel channel; + int interestOps; + int selectedOps; + int key; + boolean valid; + boolean cancelled; + + EpollSelectionKeyImpl(EpollSelectorImpl selector, + SelectableChannel channel, int fd) + { + this.selector = selector; + this.channel = channel; + this.fd = fd; + } + + /* (non-Javadoc) + * @see java.nio.channels.SelectionKey#channel() + */ + public SelectableChannel channel() + { + return channel; + } + + /* (non-Javadoc) + * @see java.nio.channels.SelectionKey#interestOps() + */ + public int interestOps() + { + return interestOps; + } + + /* (non-Javadoc) + * @see java.nio.channels.SelectionKey#interestOps(int) + */ + public SelectionKey interestOps(int ops) + { + if (cancelled) + throw new CancelledKeyException(); + if ((ops & ~(channel.validOps())) != 0) + throw new IllegalArgumentException("unsupported channel ops"); + try + { + selector.epoll_modify(this, ops); + interestOps = ops; + } + catch (IOException ioe) + { + throw new IllegalArgumentException(ioe); + } + return this; + } + + /* (non-Javadoc) + * @see java.nio.channels.SelectionKey#readyOps() + */ + public int readyOps() + { + return selectedOps; + } + + /* (non-Javadoc) + * @see java.nio.channels.SelectionKey#selector() + */ + public Selector selector() + { + return selector; + } +} diff --git a/libjava/classpath/gnu/java/nio/EpollSelectorImpl.java b/libjava/classpath/gnu/java/nio/EpollSelectorImpl.java new file mode 100644 index 00000000000..2b3c9bbb1b6 --- /dev/null +++ b/libjava/classpath/gnu/java/nio/EpollSelectorImpl.java @@ -0,0 +1,399 @@ +/* EpollSelectorImpl.java -- selector implementation using epoll + 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 gnu.classpath.Configuration; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.spi.AbstractSelectableChannel; +import java.nio.channels.spi.AbstractSelector; +import java.nio.channels.spi.SelectorProvider; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +/** + * An implementation of {@link Selector} that uses the epoll event + * notification mechanism on GNU/Linux. + * + * @author Casey Marshall (csm@gnu.org) + */ +public class EpollSelectorImpl extends AbstractSelector +{ + // XXX is this reasonable? Does it matter? + private static final int DEFAULT_EPOLL_SIZE = 128; + private static final int sizeof_struct_epoll_event; + + private static final int OP_ACCEPT = SelectionKey.OP_ACCEPT; + private static final int OP_CONNECT = SelectionKey.OP_CONNECT; + private static final int OP_READ = SelectionKey.OP_READ; + private static final int OP_WRITE = SelectionKey.OP_WRITE; + + /** our epoll file descriptor. */ + private int epoll_fd; + + private final HashMap keys; + private Set selectedKeys; + private Thread waitingThread; + private ByteBuffer events; + + private static final int INITIAL_CAPACITY; + private static final int MAX_DOUBLING_CAPACITY; + private static final int CAPACITY_INCREMENT; + + static + { + if (Configuration.INIT_LOAD_LIBRARY) + System.loadLibrary("javanio"); + + if (epoll_supported()) + sizeof_struct_epoll_event = sizeof_struct(); + else + sizeof_struct_epoll_event = -1; + + INITIAL_CAPACITY = 64 * sizeof_struct_epoll_event; + MAX_DOUBLING_CAPACITY = 1024 * sizeof_struct_epoll_event; + CAPACITY_INCREMENT = 128 * sizeof_struct_epoll_event; + } + + public EpollSelectorImpl(SelectorProvider provider) + throws IOException + { + super(provider); + epoll_fd = epoll_create(DEFAULT_EPOLL_SIZE); + keys = new HashMap(); + selectedKeys = null; + events = ByteBuffer.allocateDirect(INITIAL_CAPACITY); + } + + /* (non-Javadoc) + * @see java.nio.channels.Selector#keys() + */ + public Set keys() + { + return new HashSet(keys.values()); + } + + /* (non-Javadoc) + * @see java.nio.channels.Selector#select() + */ + public int select() throws IOException + { + return doSelect(-1); + } + + /* (non-Javadoc) + * @see java.nio.channels.Selector#select(long) + */ + public int select(long timeout) throws IOException + { + if (timeout > Integer.MAX_VALUE) + throw new IllegalArgumentException("timeout is too large"); + if (timeout < 0) + throw new IllegalArgumentException("invalid timeout"); + return doSelect((int) timeout); + } + + private int doSelect(int timeout) throws IOException + { + synchronized (keys) + { + Set cancelledKeys = cancelledKeys(); + synchronized (cancelledKeys) + { + for (Iterator it = cancelledKeys.iterator(); it.hasNext(); ) + { + EpollSelectionKeyImpl key = (EpollSelectionKeyImpl) it.next(); + epoll_delete(epoll_fd, key.fd); + key.valid = false; + keys.remove(Integer.valueOf(key.fd)); + it.remove(); + deregister(key); + } + + // Clear out closed channels. The fds are removed from the epoll + // fd when closed, so there is no need to remove them manually. + for (Iterator it = keys.values().iterator(); it.hasNext(); ) + { + EpollSelectionKeyImpl key = (EpollSelectionKeyImpl) it.next(); + SelectableChannel ch = key.channel(); + if (ch instanceof VMChannelOwner) + { + if (!((VMChannelOwner) ch).getVMChannel().getState().isValid()) + it.remove(); + } + } + + // Don't bother if we have nothing to select. + if (keys.isEmpty()) + return 0; + + int ret; + try + { + begin(); + waitingThread = Thread.currentThread(); + ret = epoll_wait(epoll_fd, events, keys.size(), timeout); + } + finally + { + Thread.interrupted(); + waitingThread = null; + end(); + } + + HashSet s = new HashSet(ret); + for (int i = 0; i < ret; i++) + { + events.position(i * sizeof_struct_epoll_event); + ByteBuffer b = events.slice(); + int fd = selected_fd(b); + EpollSelectionKeyImpl key + = (EpollSelectionKeyImpl) keys.get(Integer.valueOf(fd)); + if (key == null) + throw new IOException("fd was selected, but no key found"); + key.selectedOps = selected_ops(b) & key.interestOps; + s.add(key); + } + + reallocateBuffer(); + + selectedKeys = s; + return ret; + } + } + } + + /* (non-Javadoc) + * @see java.nio.channels.Selector#selectedKeys() + */ + public Set selectedKeys() + { + if (selectedKeys == null) + return Collections.EMPTY_SET; + return selectedKeys; + } + + /* (non-Javadoc) + * @see java.nio.channels.Selector#selectNow() + */ + public int selectNow() throws IOException + { + return doSelect(0); + } + + /* (non-Javadoc) + * @see java.nio.channels.Selector#wakeup() + */ + public Selector wakeup() + { + try + { + waitingThread.interrupt(); + } + catch (NullPointerException npe) + { + // Ignored, thrown if we are not in a blocking op. + } + return this; + } + + /* (non-Javadoc) + * @see java.nio.channels.spi.AbstractSelector#implCloseSelector() + */ + protected void implCloseSelector() throws IOException + { + VMChannel.close(epoll_fd); + } + + /* (non-Javadoc) + * @see java.nio.channels.spi.AbstractSelector#register(java.nio.channels.spi.AbstractSelectableChannel, int, java.lang.Object) + */ + protected SelectionKey register(AbstractSelectableChannel ch, int ops, Object att) + { + if (!(ch instanceof VMChannelOwner)) + throw new IllegalArgumentException("unsupported channel type"); + + VMChannel channel = ((VMChannelOwner) ch).getVMChannel(); + try + { + int native_fd = channel.getState().getNativeFD(); + synchronized (keys) + { + if (keys.containsKey(Integer.valueOf(native_fd))) + throw new IllegalArgumentException("channel already registered"); + EpollSelectionKeyImpl result = + new EpollSelectionKeyImpl(this, ch, native_fd); + if ((ops & ~(ch.validOps())) != 0) + throw new IllegalArgumentException("invalid ops for channel"); + result.interestOps = ops; + result.selectedOps = 0; + result.valid = true; + result.attach(att); + result.key = System.identityHashCode(result); + epoll_add(epoll_fd, result.fd, ops); + keys.put(Integer.valueOf(native_fd), result); + reallocateBuffer(); + return result; + } + } + catch (IOException ioe) + { + throw new IllegalArgumentException(ioe); + } + } + + private void reallocateBuffer() + { + // Ensure we have enough space for all potential events that may be + // returned. + if (events.capacity() < keys.size() * sizeof_struct_epoll_event) + { + int cap = events.capacity(); + if (cap < MAX_DOUBLING_CAPACITY) + cap <<= 1; + else + cap += CAPACITY_INCREMENT; + events = ByteBuffer.allocateDirect(cap); + } + // Ensure that the events buffer is not too large, given the number of + // events registered. + else if (events.capacity() > keys.size() * sizeof_struct_epoll_event * 2 + 1 + && events.capacity() > INITIAL_CAPACITY) + { + int cap = events.capacity() >>> 1; + events = ByteBuffer.allocateDirect(cap); + } + } + + void epoll_modify(EpollSelectionKeyImpl key, int ops) throws IOException + { + epoll_modify(epoll_fd, key.fd, ops); + } + + /** + * Tell if epoll is supported by this system, and support was compiled in. + * + * @return True if this system supports event notification with epoll. + */ + public static native boolean epoll_supported(); + + + /** + * Returns the size of `struct epoll_event'. + * + * @return The size of `struct epoll_event'. + */ + private static native int sizeof_struct(); + + + /** + * Open a new epoll file descriptor. + * + * @param size The size hint for the new epoll descriptor. + * @return The new file descriptor integer. + * @throws IOException If allocating a new epoll descriptor fails. + */ + private static native int epoll_create(int size) throws IOException; + + /** + * Add a file descriptor to this selector. + * + * @param efd The epoll file descriptor. + * @param fd The file descriptor to add (or modify). + * @param ops The interest opts. + */ + private static native void epoll_add(int efd, int fd, int ops) + throws IOException; + + /** + * Modify the interest ops of the key selecting for the given FD. + * + * @param efd The epoll file descriptor. + * @param fd The file descriptor to modify. + * @param ops The ops. + * @throws IOException + */ + private static native void epoll_modify(int efd, int fd, int ops) + throws IOException; + + /** + * Remove a file descriptor from this selector. + * + * @param efd The epoll file descriptor. + * @param fd The file descriptor. + * @throws IOException + */ + private static native void epoll_delete(int efd, int fd) throws IOException; + + /** + * Select events. + * + * @param efd The epoll file descriptor. + * @param state The buffer to hold selected events. + * @param n The number of events that may be put in `state'. + * @param timeout The timeout. + * @return The number of events selected. + * @throws IOException + */ + private static native int epoll_wait(int efd, ByteBuffer state, int n, int timeout) + throws IOException; + + /** + * Fetch the fd value from a selected struct epoll_event. + * + * @param struct The direct buffer holding the struct. + * @return The fd value. + */ + private static native int selected_fd(ByteBuffer struct); + + /** + * Fetch the enabled operations from a selected struct epoll_event. + * + * @param struct The direct buffer holding the struct. + * @return The selected operations. + */ + private static native int selected_ops(ByteBuffer struct); +} diff --git a/libjava/classpath/gnu/java/nio/channels/FileChannelImpl.java b/libjava/classpath/gnu/java/nio/FileChannelImpl.java index ed439e141ef..41912405078 100644 --- a/libjava/classpath/gnu/java/nio/channels/FileChannelImpl.java +++ b/libjava/classpath/gnu/java/nio/FileChannelImpl.java @@ -36,7 +36,7 @@ obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ -package gnu.java.nio.channels; +package gnu.java.nio; import gnu.classpath.Configuration; import gnu.java.nio.FileLockImpl; @@ -74,11 +74,11 @@ public final class FileChannelImpl extends FileChannel public static final int SYNC = 16; public static final int DSYNC = 32; - public static FileChannelImpl in; - public static FileChannelImpl out; - public static FileChannelImpl err; + public static final FileChannelImpl in; + public static final FileChannelImpl out; + public static final FileChannelImpl err; - private static native void init(); + //private static native void init(); static { @@ -87,22 +87,45 @@ public final class FileChannelImpl extends FileChannel System.loadLibrary("javanio"); } - init(); + //init(); - in = new FileChannelImpl(0, READ); - out = new FileChannelImpl(1, WRITE); - err = new FileChannelImpl(2, WRITE); + FileChannelImpl ch = null; + try + { + ch = new FileChannelImpl(VMChannel.getStdin(), READ); + } + catch (IOException ioe) + { + throw new Error(ioe); + } + in = ch; + + ch = null; + try + { + ch = new FileChannelImpl(VMChannel.getStdout(), WRITE); + } + catch (IOException ioe) + { + throw new Error(ioe); + } + out = ch; + + ch = null; + try + { + ch = new FileChannelImpl(VMChannel.getStderr(), WRITE); + } + catch (IOException ioe) + { + throw new Error(ioe); + } + err = ch; } /** * This is the actual native file descriptor value */ - // System's notion of file descriptor. It might seem redundant to - // initialize this given that it is reassigned in the constructors. - // However, this is necessary because if open() throws an exception - // we want to make sure this has the value -1. This is the most - // efficient way to accomplish that. - private int fd = -1; private VMChannel ch; private int mode; @@ -113,19 +136,19 @@ public final class FileChannelImpl extends FileChannel /* This is a static factory method, so that VM implementors can decide * substitute subclasses of FileChannelImpl. */ public static FileChannelImpl create(File file, int mode) - throws FileNotFoundException + throws IOException { return new FileChannelImpl(file, mode); } private FileChannelImpl(File file, int mode) - throws FileNotFoundException + throws IOException { String path = file.getPath(); description = path; - fd = open (path, mode); this.mode = mode; - this.ch = VMChannel.getVMChannel(this); + this.ch = new VMChannel(); + ch.openFile(path, mode); // First open the file and then check if it is a a directory // to avoid race condition. @@ -133,11 +156,11 @@ public final class FileChannelImpl extends FileChannel { try { - close(); + close(); } catch (IOException e) { - /* ignore it */ + /* ignore it */ } throw new FileNotFoundException(description + " is a directory"); @@ -153,49 +176,59 @@ public final class FileChannelImpl extends FileChannel * * @param mode READ or WRITE */ - FileChannelImpl (int fd, int mode) + FileChannelImpl (VMChannel ch, int mode) { - this.fd = fd; this.mode = mode; - this.description = "descriptor(" + fd + ")"; - this.ch = VMChannel.getVMChannel(this); + this.description = "descriptor(" + ch.getState() + ")"; + this.ch = ch; } - private native int open (String path, int mode) throws FileNotFoundException; + public int available() throws IOException + { + return ch.available(); + } + + private long implPosition() throws IOException + { + return ch.position(); + } - public native int available () throws IOException; - private native long implPosition () throws IOException; - private native void seek (long newPosition) throws IOException; - private native void implTruncate (long size) throws IOException; + private void seek(long newPosition) throws IOException + { + ch.seek(newPosition); + } + + private void implTruncate(long size) throws IOException + { + ch.truncate(size); + } - public native void unlock (long pos, long len) throws IOException; + public void unlock(long pos, long len) throws IOException + { + ch.unlock(pos, len); + } - public native long size () throws IOException; + public long size () throws IOException + { + return ch.size(); + } - protected native void implCloseChannel() throws IOException; + protected void implCloseChannel() throws IOException + { + ch.close(); + } /** * Makes sure the Channel is properly closed. */ protected void finalize() throws IOException { - if (fd != -1) + if (ch.getState().isValid()) close(); } public int read (ByteBuffer dst) throws IOException { - /* - int result; - byte[] buffer = new byte [dst.remaining ()]; - - result = read (buffer, 0, buffer.length); - - if (result > 0) - dst.put (buffer, 0, result); - - return result; - */ return ch.read(dst); } @@ -212,11 +245,10 @@ public final class FileChannelImpl extends FileChannel return result; } - public native int read () - throws IOException; - - public native int read (byte[] buffer, int offset, int length) - throws IOException; + public int read() throws IOException + { + return ch.read(); + } public long read (ByteBuffer[] dsts, int offset, int length) throws IOException @@ -252,19 +284,16 @@ public final class FileChannelImpl extends FileChannel return result; } - public native void write (byte[] buffer, int offset, int length) - throws IOException; - - public native void write (int b) throws IOException; + public void write (int b) throws IOException + { + ch.write(b); + } public long write(ByteBuffer[] srcs, int offset, int length) throws IOException { return ch.writeGathering(srcs, offset, length); } - - public native MappedByteBuffer mapImpl (char mode, long position, int size) - throws IOException; public MappedByteBuffer map (FileChannel.MapMode mode, long position, long size) @@ -291,7 +320,7 @@ public final class FileChannelImpl extends FileChannel if (position < 0 || size < 0 || size > Integer.MAX_VALUE) throw new IllegalArgumentException ("position: " + position + ", size: " + size); - return mapImpl(nmode, position, (int) size); + return ch.map(nmode, position, (int) size); } /** @@ -302,11 +331,9 @@ public final class FileChannelImpl extends FileChannel if (!isOpen ()) throw new ClosedChannelException (); - force (); + ch.flush(metaData); } - private native void force (); - // like transferTo, but with a count of less than 2Gbytes private int smallTransferTo (long position, int count, WritableByteChannel target) @@ -453,7 +480,7 @@ public final class FileChannelImpl extends FileChannel try { begin(); - boolean lockable = lock(position, size, shared, false); + boolean lockable = ch.lock(position, size, shared, false); completed = true; return (lockable ? new FileLockImpl(this, position, size, shared) @@ -464,14 +491,6 @@ public final class FileChannelImpl extends FileChannel end(completed); } } - - /** Try to acquire a lock at the given position and size. - * On success return true. - * If wait as specified, block until we can get it. - * Otherwise return false. - */ - private native boolean lock(long position, long size, - boolean shared, boolean wait) throws IOException; public FileLock lock (long position, long size, boolean shared) throws IOException @@ -481,7 +500,7 @@ public final class FileChannelImpl extends FileChannel boolean completed = false; try { - boolean lockable = lock(position, size, shared, true); + boolean lockable = ch.lock(position, size, shared, true); completed = true; return (lockable ? new FileLockImpl(this, position, size, shared) @@ -537,17 +556,17 @@ public final class FileChannelImpl extends FileChannel public String toString() { - return (this.getClass() - + "[fd=" + fd - + ",mode=" + mode + "," - + description + "]"); + return (super.toString() + + "[ fd: " + ch.getState() + + "; mode: " + Integer.toOctalString(mode) + + "; " + description + " ]"); } /** * @return The native file descriptor. - */ + * / public int getNativeFD() { return fd; - } + }*/ } diff --git a/libjava/classpath/gnu/java/nio/FileLockImpl.java b/libjava/classpath/gnu/java/nio/FileLockImpl.java index 673ca2522df..768906ce973 100644 --- a/libjava/classpath/gnu/java/nio/FileLockImpl.java +++ b/libjava/classpath/gnu/java/nio/FileLockImpl.java @@ -38,8 +38,6 @@ exception statement from your version. */ package gnu.java.nio; -import gnu.java.nio.channels.FileChannelImpl; - import java.io.IOException; import java.nio.channels.FileLock; diff --git a/libjava/classpath/gnu/java/nio/KqueueSelectionKeyImpl.java b/libjava/classpath/gnu/java/nio/KqueueSelectionKeyImpl.java new file mode 100644 index 00000000000..2f93c50cc19 --- /dev/null +++ b/libjava/classpath/gnu/java/nio/KqueueSelectionKeyImpl.java @@ -0,0 +1,189 @@ +/* KqueueSelectionKeyImpl.java -- selection key for kqueue/kevent. + 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.nio.ByteBuffer; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.spi.AbstractSelectionKey; + +/** + * @author Casey Marshall (csm@gnu.org) + */ +public class KqueueSelectionKeyImpl extends AbstractSelectionKey +{ + int interestOps; + int readyOps; + int activeOps = 0; + int key; + int fd; + + /** The selector we were created for. */ + private final KqueueSelectorImpl selector; + + /** The channel we are attached to. */ + private final SelectableChannel channel; + + private final VMChannelOwner natChannel; + + public KqueueSelectionKeyImpl(KqueueSelectorImpl selector, + SelectableChannel channel) + { + this.selector = selector; + this.channel = channel; + natChannel = (VMChannelOwner) channel; + interestOps = 0; + readyOps = 0; + } + + /* (non-Javadoc) + * @see java.nio.channels.SelectionKey#channel() + */ + //@Override + public SelectableChannel channel() + { + return channel; + } + + /* (non-Javadoc) + * @see java.nio.channels.SelectionKey#interestOps() + */ + //@Override + public int interestOps() + { + return interestOps; + } + + /* (non-Javadoc) + * @see java.nio.channels.SelectionKey#interestOps(int) + */ + //@Override + public SelectionKey interestOps(int ops) + { + if (!isValid()) + throw new IllegalStateException("key is invalid"); + if ((ops & ~channel.validOps()) != 0) + throw new IllegalArgumentException("channel does not support all operations"); + + selector.setInterestOps(this, ops); + return this; + } + + /* (non-Javadoc) + * @see java.nio.channels.SelectionKey#readyOps() + */ + //@Override + public int readyOps() + { + return readyOps; + } + + /* (non-Javadoc) + * @see java.nio.channels.SelectionKey#selector() + */ + //@Override + public Selector selector() + { + return selector; + } + + public String toString() + { + if (!isValid()) + return super.toString() + " [ fd: " + fd + " <<invalid>> ]"; + return super.toString() + " [ fd: " + fd + " interest ops: {" + + ((interestOps & OP_ACCEPT) != 0 ? " OP_ACCEPT" : "") + + ((interestOps & OP_CONNECT) != 0 ? " OP_CONNECT" : "") + + ((interestOps & OP_READ) != 0 ? " OP_READ" : "") + + ((interestOps & OP_WRITE) != 0 ? " OP_WRITE" : "") + + " }; ready ops: {" + + ((readyOps & OP_ACCEPT) != 0 ? " OP_ACCEPT" : "") + + ((readyOps & OP_CONNECT) != 0 ? " OP_CONNECT" : "") + + ((readyOps & OP_READ) != 0 ? " OP_READ" : "") + + ((readyOps & OP_WRITE) != 0 ? " OP_WRITE" : "") + + " } ]"; + } + + public int hashCode() + { + return fd; + } + + public boolean equals(Object o) + { + if (!(o instanceof KqueueSelectionKeyImpl)) + return false; + KqueueSelectionKeyImpl that = (KqueueSelectionKeyImpl) o; + return that.fd == this.fd && that.channel.equals(this.channel); + } + + + boolean isReadActive() + { + return (activeOps & (OP_READ | OP_ACCEPT)) != 0; + } + + boolean isReadInterested() + { + return (interestOps & (OP_READ | OP_ACCEPT)) != 0; + } + + boolean isWriteActive() + { + return (activeOps & (OP_WRITE | OP_CONNECT)) != 0; + } + + boolean isWriteInterested() + { + return (interestOps & (OP_WRITE | OP_CONNECT)) != 0; + } + + boolean needCommitRead() + { + return isReadActive() == (!isReadInterested()); + } + + boolean needCommitWrite() + { + return isWriteActive() == (!isWriteInterested()); + } +} diff --git a/libjava/classpath/gnu/java/nio/KqueueSelectorImpl.java b/libjava/classpath/gnu/java/nio/KqueueSelectorImpl.java new file mode 100644 index 00000000000..34ca1dc596d --- /dev/null +++ b/libjava/classpath/gnu/java/nio/KqueueSelectorImpl.java @@ -0,0 +1,527 @@ +/* KqueueSelectorImpl.java -- Selector for systems with kqueue event notification. + 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.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.ClosedSelectorException; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.spi.AbstractSelectableChannel; +import java.nio.channels.spi.AbstractSelector; +import java.nio.channels.spi.SelectorProvider; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * A {@link Selector} implementation that uses the <code>kqueue</code> + * event notification facility. + * + * @author Casey Marshall (csm@gnu.org) + */ +public class KqueueSelectorImpl extends AbstractSelector +{ + // Prepended underscore to field name to make it distinct + // from the method with the similar name. + private static final int _sizeof_struct_kevent; + + private static final int MAX_DOUBLING_CAPACITY = 16384; + private static final int CAP_INCREMENT = 1024; + private static final int INITIAL_CAPACITY; + + static + { + try + { + System.loadLibrary("javanio"); + } + catch (Exception x) + { + x.printStackTrace(); + } + + if (kqueue_supported ()) + _sizeof_struct_kevent = sizeof_struct_kevent(); + else + _sizeof_struct_kevent = -1; + INITIAL_CAPACITY = 16 * _sizeof_struct_kevent; + } + + /** + * Tell if kqueue-based selectors are supported on this system. + * + * @return True if this system has kqueue support, and support for it was + * compiled in to Classpath. + */ + public static native boolean kqueue_supported(); + + /* Our native file descriptor. */ + private int kq; + + private HashMap/*<Integer,KqueueSelectionKeyImpl>*/ keys; + private HashSet/*<KqueueSelectionKeyImpl>*/ selected; + private Thread blockedThread; + private ByteBuffer events; + + private static final int OP_ACCEPT = SelectionKey.OP_ACCEPT; + private static final int OP_CONNECT = SelectionKey.OP_CONNECT; + private static final int OP_READ = SelectionKey.OP_READ; + private static final int OP_WRITE = SelectionKey.OP_WRITE; + + public KqueueSelectorImpl(SelectorProvider provider) throws IOException + { + super(provider); + kq = implOpen(); + keys = new HashMap/*<KqueueSelectionKeyImpl>*/(); + events = ByteBuffer.allocateDirect(INITIAL_CAPACITY); + } + + protected void implCloseSelector() throws IOException + { + implClose(kq); + kq = -1; + } + + /* (non-Javadoc) + * @see java.nio.channels.Selector#keys() + */ + public Set keys() + { + if (!isOpen()) + throw new ClosedSelectorException(); + + return new HashSet(keys.values()); + } + + /* (non-Javadoc) + * @see java.nio.channels.Selector#select() + */ + public int select() throws IOException + { + return doSelect(-1); + } + + /* (non-Javadoc) + * @see java.nio.channels.Selector#select(long) + */ + public int select(long timeout) throws IOException + { + if (timeout == 0) + timeout = -1; + return doSelect(timeout); + } + + /* (non-Javadoc) + * @see java.nio.channels.Selector#selectedKeys() + */ + public Set selectedKeys() + { + if (!isOpen()) + throw new ClosedSelectorException(); + + return selected; + } + + /* (non-Javadoc) + * @see java.nio.channels.Selector#selectNow() + */ + public int selectNow() throws IOException + { + return doSelect(0); + } + + /* (non-Javadoc) + * @see java.nio.channels.Selector#wakeup() + */ + public Selector wakeup() + { + if (blockedThread != null) + blockedThread.interrupt(); + return this; + } + + public String toString() + { + return super.toString() + " [ fd: " + kq + " ]"; + } + + public boolean equals(Object o) + { + if (!(o instanceof KqueueSelectorImpl)) + return false; + + return ((KqueueSelectorImpl) o).kq == kq; + } + + int doSelect(long timeout) throws IOException + { + Set cancelled = cancelledKeys(); + synchronized (cancelled) + { + synchronized (keys) + { + for (Iterator it = cancelled.iterator(); it.hasNext(); ) + { + KqueueSelectionKeyImpl key = (KqueueSelectionKeyImpl) it.next(); + key.interestOps = 0; + } + + int events_size = (2 * _sizeof_struct_kevent) * keys.size(); + int num_events = 0; + + for (Iterator it = keys.entrySet().iterator(); it.hasNext(); ) + { + Map.Entry e = (Map.Entry) it.next(); + KqueueSelectionKeyImpl key = (KqueueSelectionKeyImpl) e.getValue(); + + SelectableChannel ch = key.channel(); + if (ch instanceof VMChannelOwner) + { + if (!((VMChannelOwner) ch).getVMChannel().getState().isValid()) + { + // closed channel; removed from kqueue automatically. + it.remove(); + continue; + } + } + + // If this key is registering a read filter, add it to the buffer. + if (key.needCommitRead()) + { + kevent_set(events, num_events, key.fd, + key.interestOps & (OP_READ | OP_ACCEPT), + key.activeOps & (OP_READ | OP_ACCEPT), key.key); + num_events++; + } + + // If this key is registering a write filter, add it to the buffer. + if (key.needCommitWrite()) + { + kevent_set(events, num_events, key.fd, + key.interestOps & (OP_WRITE | OP_CONNECT), + key.activeOps & (OP_WRITE | OP_CONNECT), key.key); + num_events++; + } + } + events.rewind().limit(events.capacity()); + + //System.out.println("dump of keys to select:"); + //dump_selection_keys(events.duplicate()); + + int n = 0; + try + { + //System.out.println("[" + kq + "] kevent enter selecting from " + keys.size()); + begin(); + blockedThread = Thread.currentThread(); + if (blockedThread.isInterrupted()) + timeout = 0; + n = kevent(kq, events, num_events, + events.capacity() / _sizeof_struct_kevent, timeout); + } + finally + { + end(); + blockedThread = null; + Thread.interrupted(); + //System.out.println("[" + kq + "kevent exit selected " + n); + } + + //System.out.println("dump of keys selected:"); + //dump_selection_keys((ByteBuffer) events.duplicate().limit(n * _sizeof_struct_kevent)); + + // Commit the operations we've just added in the call to kevent. + for (Iterator it = keys.values().iterator(); it.hasNext(); ) + { + KqueueSelectionKeyImpl key = (KqueueSelectionKeyImpl) it.next(); + key.activeOps = key.interestOps; + } + + selected = new HashSet/*<KqueueSelectionKeyImpl>*/(n); + int x = 0; + for (int i = 0; i < n; i++) + { + events.position(x).limit(x + _sizeof_struct_kevent); + x += _sizeof_struct_kevent; + int y = fetch_key(events.slice()); + KqueueSelectionKeyImpl key = + (KqueueSelectionKeyImpl) keys.get(new Integer(y)); + + if (key == null) + { + System.out.println("WARNING! no key found for selected key " + y); + continue; + } + // Keys that have been cancelled may be returned here; don't + // add them to the selected set. + if (!key.isValid()) + continue; + key.readyOps = ready_ops(events.slice(), key.interestOps); + selected.add(key); + } + + // Finally, remove the cancelled keys. + for (Iterator it = cancelled.iterator(); it.hasNext(); ) + { + KqueueSelectionKeyImpl key = (KqueueSelectionKeyImpl) it.next(); + keys.remove(new Integer(key.key)); + deregister(key); + it.remove(); + } + + reallocateBuffer(); + + return selected.size(); + } + } + } + + protected SelectionKey register(AbstractSelectableChannel channel, + int interestOps, + Object attachment) + { + int native_fd = -1; + try + { + if (channel instanceof VMChannelOwner) + native_fd = ((VMChannelOwner) channel).getVMChannel() + .getState().getNativeFD(); + else + throw new IllegalArgumentException("cannot handle channel type " + + channel.getClass().getName()); + } + catch (IOException ioe) + { + throw new IllegalArgumentException("channel is closed or invalid"); + } + + KqueueSelectionKeyImpl result = new KqueueSelectionKeyImpl(this, channel); + result.interestOps = interestOps; + result.attach(attachment); + result.fd = native_fd; + result.key = System.identityHashCode(result); + synchronized (keys) + { + while (keys.containsKey(new Integer(result.key))) + result.key++; + keys.put(new Integer(result.key), result); + reallocateBuffer(); + } + return result; + } + + void setInterestOps(KqueueSelectionKeyImpl key, int ops) + { + synchronized (keys) + { + key.interestOps = ops; + } + } + + /** + * Reallocate the events buffer. This is the destination buffer for + * events returned by kevent. This method will: + * + * * Grow the buffer if there is insufficent space for all registered + * events. + * * Shrink the buffer if it is more than twice the size needed. + * + */ + private void reallocateBuffer() + { + synchronized (keys) + { + if (events.capacity() < (2 * _sizeof_struct_kevent) * keys.size()) + { + int cap = events.capacity(); + if (cap >= MAX_DOUBLING_CAPACITY) + cap += CAP_INCREMENT; + else + cap = cap << 1; + + events = ByteBuffer.allocateDirect(cap); + } + else if (events.capacity() > 4 * (_sizeof_struct_kevent) * keys.size() + 1 + && events.capacity() > INITIAL_CAPACITY) + { + int cap = events.capacity(); + cap = cap >>> 1; + events = ByteBuffer.allocateDirect(cap); + } + } + } + + //synchronized void updateOps(KqueueSelectionKeyImpl key, int interestOps) + //{ + // updateOps(key, interestOps, 0, false); + //} + + /*void updateOps(KqueueSelectionKeyImpl key, int interestOps, + int activeOps, int fd) + { + //System.out.println(">> updating kqueue selection key:"); + //dump_selection_keys(key.nstate.duplicate()); + //System.out.println("<<"); + synchronized (keys) + { + kevent_set(key.nstate, fd, interestOps, activeOps, key.key); + } + //System.out.println(">> updated kqueue selection key:"); + //dump_selection_keys(key.nstate.duplicate()); + //System.out.println("<<"); + }*/ + + private void dump_selection_keys(ByteBuffer keys) + { + // WARNING! This method is not guaranteed to be portable! This works + // on darwin/x86, but the sizeof and offsetof these fields may be + // different on other platforms! + int i = 0; + keys.order(ByteOrder.nativeOrder()); + while (keys.hasRemaining()) + { + System.out.println("struct kevent { ident: " + + Integer.toString(keys.getInt()) + + " filter: " + + Integer.toHexString(keys.getShort() & 0xFFFF) + + " flags: " + + Integer.toHexString(keys.getShort() & 0xFFFF) + + " fflags: " + + Integer.toHexString(keys.getInt()) + + " data: " + + Integer.toHexString(keys.getInt()) + + " udata: " + + Integer.toHexString(keys.getInt()) + + " }"); + } + } + + /** + * Return the size of a <code>struct kevent</code> on this system. + * + * @return The size of <code>struct kevent</code>. + */ + private static native int sizeof_struct_kevent(); + + /** + * Opens a kqueue descriptor. + * + * @return The new kqueue descriptor. + * @throws IOException If opening fails. + */ + private static native int implOpen() throws IOException; + + /** + * Closes the kqueue file descriptor. + * + * @param kq The kqueue file descriptor. + * @throws IOException + */ + private static native void implClose(int kq) throws IOException; + + /** + * Initialize the specified native state for the given interest ops. + * + * @param nstate The native state structures; in this buffer should be + * the <code>struct kevent</code>s created for a key. + * @param fd The file descriptor. If 0, the native FD is unmodified. + * @param interestOps The operations to enable. + * @param key A unique key that will reference the associated key later. + * @param delete Set to true if this event should be deleted from the + * kqueue (if false, this event is added/updated). + */ + private static native void kevent_set(ByteBuffer nstate, int i, int fd, + int interestOps, int activeOps, int key); + + /** + * Poll for events. The source events are stored in <code>events</code>, + * which is also where polled events will be placed. + * + * @param events The events to poll. This buffer is also the destination + * for events read from the queue. + * @param nevents The number of events to poll (that is, the number of + * events in the <code>events</code> buffer). + * @param nout The maximum number of events that may be returned. + * @param timeout The timeout. A timeout of -1 returns immediately; a timeout + * of 0 waits indefinitely. + * @return The number of events read. + */ + private static native int kevent(int kq, ByteBuffer events, int nevents, + int nout, long timeout); + + /** + * Fetch a polled key from a native state buffer. For each kevent key we + * create, we put the native state info (one or more <code>struct + * kevent</code>s) in that key's {@link KqueueSelectionKeyImpl#nstate} + * buffer, and place the pointer of the key in the <code>udata</code> field + * of that structure. This method fetches that pointer from the given + * buffer (assumed to be a <code>struct kqueue</code>) and returns it. + * + * @param nstate The buffer containing the <code>struct kqueue</code> to read. + * @return The key object. + */ + private static native int fetch_key(ByteBuffer nstate); + + /** + * Fetch the ready ops of the associated native state. That is, this + * inspects the first argument as a <code>struct kevent</code>, looking + * at its operation (the input is assumed to have been returned via a + * previous call to <code>kevent</code>), and translating that to the + * appropriate Java bit set, based on the second argument. + * + * @param nstate The native state. + * @param interestOps The enabled operations for the key. + * @return The bit set representing the ready operations. + */ + private static native int ready_ops(ByteBuffer nstate, int interestOps); + + /** + * Check if kevent returned EV_EOF for a selection key. + * + * @param nstate The native state. + * @return True if the kevent call returned EOF. + */ + private static native boolean check_eof(ByteBuffer nstate); +} diff --git a/libjava/classpath/gnu/java/nio/NIOSocket.java b/libjava/classpath/gnu/java/nio/NIOSocket.java index 4d812bf44ba..060a3a89c1c 100644 --- a/libjava/classpath/gnu/java/nio/NIOSocket.java +++ b/libjava/classpath/gnu/java/nio/NIOSocket.java @@ -48,30 +48,33 @@ import java.nio.channels.SocketChannel; */ public final class NIOSocket extends Socket { - private PlainSocketImpl impl; private SocketChannelImpl channel; - protected NIOSocket (PlainSocketImpl impl, SocketChannelImpl channel) + protected NIOSocket (SocketChannelImpl channel) throws IOException { - super (impl); - this.impl = impl; + super (new NIOSocketImpl(channel)); this.channel = channel; } - public final PlainSocketImpl getPlainSocketImpl() - { - return impl; - } + //public final PlainSocketImpl getPlainSocketImpl() + //{ + // return impl; + //} - final void setChannel (SocketChannelImpl channel) - { - this.impl = channel.getPlainSocketImpl(); - this.channel = channel; - } + //final void setChannel (SocketChannelImpl channel) + //{ + // this.impl = channel.getPlainSocketImpl(); + // this.channel = channel; + //} public final SocketChannel getChannel() { return channel; } + + public boolean isConnected() + { + return channel.isConnected(); + } } diff --git a/libjava/classpath/gnu/java/nio/NIOSocketImpl.java b/libjava/classpath/gnu/java/nio/NIOSocketImpl.java new file mode 100644 index 00000000000..4b26561a212 --- /dev/null +++ b/libjava/classpath/gnu/java/nio/NIOSocketImpl.java @@ -0,0 +1,110 @@ +/* NIOSocketImpl.java -- subclass of PlainSocketImpl for NIO. + 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 gnu.java.net.PlainSocketImpl; + +import java.io.IOException; +import java.net.InetAddress; + +/** + * @author Casey Marshall (csm@gnu.org) + */ +public class NIOSocketImpl extends PlainSocketImpl +{ + + private final SocketChannelImpl channel; + + NIOSocketImpl(SocketChannelImpl channel) throws IOException + { + this.channel = channel; + impl.getState().setChannelFD(channel.getVMChannel().getState()); + } + + /* (non-Javadoc) + * @see java.net.SocketImpl#getInetAddress() + */ + //@Override + protected InetAddress getInetAddress() + { + try + { + return channel.getVMChannel().getPeerAddress().getAddress(); + } + catch (IOException ioe) + { + return null; + } + catch (NullPointerException npe) + { + // Socket is not connected yet. + return null; + } + } + + /* (non-Javadoc) + * @see java.net.SocketImpl#getPort() + */ + //@Override + protected int getPort() + { + try + { + return channel.getVMChannel().getPeerAddress().getPort(); + } + catch (IOException ioe) + { + return -1; + } + catch (NullPointerException npe) + { + // Socket is not connected yet. + return -1; + } + } + + /* (non-Javadoc) + * @see gnu.java.net.PlainSocketImpl#create(boolean) + */ + //@Override + protected synchronized void create(boolean stream) + { + // Ignored; the socket has already been created. + } +} diff --git a/libjava/classpath/gnu/java/nio/PipeImpl.java b/libjava/classpath/gnu/java/nio/PipeImpl.java index cccaa39885f..8a95b9622ae 100644 --- a/libjava/classpath/gnu/java/nio/PipeImpl.java +++ b/libjava/classpath/gnu/java/nio/PipeImpl.java @@ -46,22 +46,21 @@ import java.nio.channels.spi.SelectorProvider; class PipeImpl extends Pipe { public static final class SourceChannelImpl extends Pipe.SourceChannel + implements VMChannelOwner { - private int native_fd; private VMChannel vmch; public SourceChannelImpl (SelectorProvider selectorProvider, - int native_fd) + VMChannel channel) { super (selectorProvider); - this.native_fd = native_fd; - vmch = VMChannel.getVMChannel(this); + vmch = channel; } protected final void implCloseSelectableChannel() throws IOException { - throw new Error ("Not implemented"); + vmch.close(); } protected void implConfigureBlocking (boolean blocking) @@ -94,30 +93,29 @@ class PipeImpl extends Pipe return vmch.readScattering(srcs, offset, len); } - - public final int getNativeFD() + + public VMChannel getVMChannel() { - return native_fd; + return vmch; } } public static final class SinkChannelImpl extends Pipe.SinkChannel + implements VMChannelOwner { - private int native_fd; private VMChannel vmch; public SinkChannelImpl (SelectorProvider selectorProvider, - int native_fd) + VMChannel channel) { super (selectorProvider); - this.native_fd = native_fd; - vmch = VMChannel.getVMChannel(this); + vmch = channel; } protected final void implCloseSelectableChannel() throws IOException { - throw new Error ("Not implemented"); + vmch.close(); } protected final void implConfigureBlocking (boolean blocking) @@ -149,10 +147,10 @@ class PipeImpl extends Pipe return vmch.writeGathering(srcs, offset, len); } - - public final int getNativeFD() + + public VMChannel getVMChannel() { - return native_fd; + return vmch; } } @@ -163,7 +161,9 @@ class PipeImpl extends Pipe throws IOException { super(); - VMPipe.init (this, provider); + VMChannel[] pipe = VMPipe.pipe(); + sink = new SinkChannelImpl(provider, pipe[0]); + source = new SourceChannelImpl(provider, pipe[1]); } public Pipe.SinkChannel sink() diff --git a/libjava/classpath/gnu/java/nio/SelectionKeyImpl.java b/libjava/classpath/gnu/java/nio/SelectionKeyImpl.java index 8745377c58f..c927f319644 100644 --- a/libjava/classpath/gnu/java/nio/SelectionKeyImpl.java +++ b/libjava/classpath/gnu/java/nio/SelectionKeyImpl.java @@ -106,5 +106,6 @@ public abstract class SelectionKeyImpl extends AbstractSelectionKey return impl; } + /* @deprecated */ public abstract int getNativeFD(); } diff --git a/libjava/classpath/gnu/java/nio/SelectorImpl.java b/libjava/classpath/gnu/java/nio/SelectorImpl.java index d0ec4871367..c08478c9968 100644 --- a/libjava/classpath/gnu/java/nio/SelectorImpl.java +++ b/libjava/classpath/gnu/java/nio/SelectorImpl.java @@ -54,8 +54,8 @@ import java.util.Set; public class SelectorImpl extends AbstractSelector { - private Set keys; - private Set selected; + private Set<SelectionKey> keys; + private Set<SelectionKey> selected; /** * A dummy object whose monitor regulates access to both our @@ -83,8 +83,8 @@ public class SelectorImpl extends AbstractSelector { super (provider); - keys = new HashSet (); - selected = new HashSet (); + keys = new HashSet<SelectionKey> (); + selected = new HashSet<SelectionKey> (); } protected void finalize() throws Throwable @@ -110,7 +110,7 @@ public class SelectorImpl extends AbstractSelector } } - public final Set keys() + public final Set<SelectionKey> keys() { if (!isOpen()) throw new ClosedSelectorException(); @@ -136,7 +136,7 @@ public class SelectorImpl extends AbstractSelector { int[] result; int counter = 0; - Iterator it = keys.iterator (); + Iterator<SelectionKey> it = keys.iterator (); // Count the number of file descriptors needed while (it.hasNext ()) @@ -253,7 +253,7 @@ public class SelectorImpl extends AbstractSelector selectThread = null; } - Iterator it = keys.iterator (); + Iterator<SelectionKey> it = keys.iterator (); while (it.hasNext ()) { @@ -317,7 +317,7 @@ public class SelectorImpl extends AbstractSelector } } - public final Set selectedKeys() + public final Set<SelectionKey> selectedKeys() { if (!isOpen()) throw new ClosedSelectorException(); @@ -350,10 +350,10 @@ public class SelectorImpl extends AbstractSelector private final void deregisterCancelledKeys() { - Set ckeys = cancelledKeys (); + Set<SelectionKey> ckeys = cancelledKeys (); synchronized (ckeys) { - Iterator it = ckeys.iterator(); + Iterator<SelectionKey> it = ckeys.iterator(); while (it.hasNext ()) { diff --git a/libjava/classpath/gnu/java/nio/SelectorProviderImpl.java b/libjava/classpath/gnu/java/nio/SelectorProviderImpl.java index 47521107e90..56167b69ea8 100644 --- a/libjava/classpath/gnu/java/nio/SelectorProviderImpl.java +++ b/libjava/classpath/gnu/java/nio/SelectorProviderImpl.java @@ -37,6 +37,9 @@ exception statement from your version. */ package gnu.java.nio; + +import gnu.classpath.SystemProperties; + import java.io.IOException; import java.nio.channels.DatagramChannel; import java.nio.channels.Pipe; @@ -47,6 +50,11 @@ import java.nio.channels.spi.SelectorProvider; public class SelectorProviderImpl extends SelectorProvider { + private static final String SELECTOR_IMPL_KQUEUE = "kqueue"; + private static final String SELECTOR_IMPL_EPOLL = "epoll"; + private static final String SELECTOR_IMPL = "gnu.java.nio.selectorImpl"; + private static boolean epoll_failed = false; + public SelectorProviderImpl () { } @@ -66,6 +74,35 @@ public class SelectorProviderImpl extends SelectorProvider public AbstractSelector openSelector () throws IOException { + String selectorImpl = "default"; + if (KqueueSelectorImpl.kqueue_supported()) + selectorImpl = SELECTOR_IMPL_KQUEUE; + if (EpollSelectorImpl.epoll_supported() && !epoll_failed) + selectorImpl = SELECTOR_IMPL_EPOLL; + selectorImpl = SystemProperties.getProperty(SELECTOR_IMPL, selectorImpl); + + if (selectorImpl.equals(SELECTOR_IMPL_KQUEUE)) + return new KqueueSelectorImpl(this); + + if (selectorImpl.equals(SELECTOR_IMPL_EPOLL)) + { + // We jump through these hoops because even though epoll may look + // like it's available (sys/epoll.h exists, and you can link against + // all the epoll functions) it may not be available in the kernel + // (especially 2.4 kernels), meaning you will get ENOSYS at run time. + // + // Madness! + try + { + return new EpollSelectorImpl(this); + } + catch (InternalError e) + { + // epoll_create throws this on ENOSYS. + epoll_failed = true; + } + } + return new SelectorImpl (this); } diff --git a/libjava/classpath/gnu/java/nio/ServerSocketChannelImpl.java b/libjava/classpath/gnu/java/nio/ServerSocketChannelImpl.java index c538ea802e1..1e8e0901d7c 100644 --- a/libjava/classpath/gnu/java/nio/ServerSocketChannelImpl.java +++ b/libjava/classpath/gnu/java/nio/ServerSocketChannelImpl.java @@ -48,7 +48,9 @@ import java.nio.channels.SocketChannel; import java.nio.channels.spi.SelectorProvider; public final class ServerSocketChannelImpl extends ServerSocketChannel + implements VMChannelOwner { + private VMChannel channel; private NIOServerSocket serverSocket; private boolean connected; @@ -56,13 +58,15 @@ public final class ServerSocketChannelImpl extends ServerSocketChannel throws IOException { super (provider); - serverSocket = new NIOServerSocket (this); + serverSocket = new NIOServerSocket(this); + channel = serverSocket.getPlainSocketImpl().getVMChannel(); configureBlocking(true); } + // XXX do we need this? public void finalizer() { - if (connected) + if (channel.getState().isValid()) { try { @@ -77,12 +81,12 @@ public final class ServerSocketChannelImpl extends ServerSocketChannel protected void implCloseSelectableChannel () throws IOException { connected = false; - serverSocket.close(); + channel.close(); } protected void implConfigureBlocking (boolean blocking) throws IOException { - serverSocket.setSoTimeout (blocking ? 0 : NIOConstants.DEFAULT_TIMEOUT); + channel.setBlocking(blocking); } public SocketChannel accept () throws IOException @@ -98,27 +102,28 @@ public final class ServerSocketChannelImpl extends ServerSocketChannel try { begin(); - serverSocket.getPlainSocketImpl().setInChannelOperation(true); - // indicate that a channel is initiating the accept operation - // so that the socket ignores the fact that we might be in - // non-blocking mode. - NIOSocket socket = (NIOSocket) serverSocket.accept(); - completed = true; - return socket.getChannel(); - } - catch (SocketTimeoutException e) - { - return null; + VMChannel client = channel.accept(); + if (client == null) + return null; + else + { + completed = true; + return new SocketChannelImpl(provider(), client, false); + } } finally { - serverSocket.getPlainSocketImpl().setInChannelOperation(false); end (completed); } } - public ServerSocket socket () + public ServerSocket socket() { return serverSocket; } + + public VMChannel getVMChannel() + { + return channel; + } } diff --git a/libjava/classpath/gnu/java/nio/ServerSocketChannelSelectionKey.java b/libjava/classpath/gnu/java/nio/ServerSocketChannelSelectionKey.java index d00c2b7482a..5b510cb6f4d 100644 --- a/libjava/classpath/gnu/java/nio/ServerSocketChannelSelectionKey.java +++ b/libjava/classpath/gnu/java/nio/ServerSocketChannelSelectionKey.java @@ -38,6 +38,7 @@ exception statement from your version. */ package gnu.java.nio; +import java.io.IOException; import java.nio.channels.spi.AbstractSelectableChannel; public final class ServerSocketChannelSelectionKey @@ -49,10 +50,16 @@ public final class ServerSocketChannelSelectionKey super (channel, selector); } + // FIXME don't use file descriptor integers public int getNativeFD() { - NIOServerSocket socket = - (NIOServerSocket) ((ServerSocketChannelImpl) ch).socket(); - return socket.getPlainSocketImpl().getNativeFD(); + try + { + return ((ServerSocketChannelImpl) ch).getVMChannel().getState().getNativeFD(); + } + catch (IOException ioe) + { + throw new IllegalStateException(ioe); + } } } diff --git a/libjava/classpath/gnu/java/nio/SocketChannelImpl.java b/libjava/classpath/gnu/java/nio/SocketChannelImpl.java index 680eba2f92b..1c563ac097c 100644 --- a/libjava/classpath/gnu/java/nio/SocketChannelImpl.java +++ b/libjava/classpath/gnu/java/nio/SocketChannelImpl.java @@ -39,15 +39,20 @@ exception statement from your version. */ package gnu.java.nio; import gnu.java.net.PlainSocketImpl; +import gnu.java.net.VMPlainSocketImpl; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; +import java.net.SocketException; import java.net.SocketTimeoutException; import java.nio.ByteBuffer; +import java.nio.ReadOnlyBufferException; import java.nio.channels.AlreadyConnectedException; import java.nio.channels.ClosedChannelException; import java.nio.channels.ConnectionPendingException; @@ -61,28 +66,57 @@ import java.nio.channels.UnsupportedAddressTypeException; import java.nio.channels.spi.SelectorProvider; public final class SocketChannelImpl extends SocketChannel + implements VMChannelOwner { - private PlainSocketImpl impl; + private VMChannel channel; + //private PlainSocketImpl impl; private NIOSocket socket; private boolean connectionPending; + private boolean connected; + private InetSocketAddress connectAddress; + + public SocketChannelImpl(boolean create) throws IOException + { + // XXX consider adding security check; this is used by + // PlainSocketImpl. + this(new SelectorProviderImpl(), create); + } + + public SocketChannelImpl(VMChannel channel) throws IOException + { + this(new SelectorProviderImpl(), channel, false); + } + + SocketChannelImpl(SelectorProvider provider) throws IOException + { + this(provider, true); + } - SocketChannelImpl (SelectorProvider provider) + SocketChannelImpl(SelectorProvider provider, boolean create) + throws IOException + { + this(provider, new VMChannel(), create); + } + + SocketChannelImpl(SelectorProvider provider, VMChannel channel, boolean create) throws IOException { super (provider); - impl = new PlainSocketImpl(); - socket = new NIOSocket (impl, this); + this.channel = channel; + if (create) + channel.initSocket(true); + socket = new NIOSocket(this); configureBlocking(true); } - SocketChannelImpl (SelectorProvider provider, + /*SocketChannelImpl (SelectorProvider provider, NIOSocket socket) throws IOException { super (provider); this.impl = socket.getPlainSocketImpl(); this.socket = socket; - } + }*/ public void finalizer() { @@ -98,23 +132,28 @@ public final class SocketChannelImpl extends SocketChannel } } - PlainSocketImpl getPlainSocketImpl() - { - return impl; - } + //PlainSocketImpl getPlainSocketImpl() + //{ + // return null; // XXX + //} - protected void implCloseSelectableChannel () throws IOException + protected void implCloseSelectableChannel() throws IOException { - socket.close(); + channel.close(); } protected void implConfigureBlocking (boolean blocking) throws IOException { - socket.setSoTimeout (blocking ? 0 : NIOConstants.DEFAULT_TIMEOUT); + channel.setBlocking(blocking); } public boolean connect (SocketAddress remote) throws IOException { + return connect(remote, 0); + } + + public boolean connect (SocketAddress remote, int timeout) throws IOException + { if (!isOpen()) throw new ClosedChannelException(); @@ -126,79 +165,52 @@ public final class SocketChannelImpl extends SocketChannel if (!(remote instanceof InetSocketAddress)) throw new UnsupportedAddressTypeException(); + + connectAddress = (InetSocketAddress) remote; - if (((InetSocketAddress) remote).isUnresolved()) + if (connectAddress.isUnresolved()) throw new UnresolvedAddressException(); - try - { - socket.getPlainSocketImpl().setInChannelOperation(true); - // indicate that a channel is initiating the accept operation - // so that the socket ignores the fact that we might be in - // non-blocking mode. - - if (isBlocking()) - { - // Do blocking connect. - socket.connect (remote); - return true; - } - - // Do non-blocking connect. - try - { - socket.connect (remote, NIOConstants.DEFAULT_TIMEOUT); - return true; - } - catch (SocketTimeoutException e) - { - connectionPending = true; - return false; - } - } - finally - { - socket.getPlainSocketImpl().setInChannelOperation(false); - } + connected = channel.connect(connectAddress, timeout); + connectionPending = !connected; + return connected; } - - public boolean finishConnect () + + public boolean finishConnect() throws IOException { if (!isOpen()) throw new ClosedChannelException(); - - if (!isConnected() && !connectionPending) - throw new NoConnectionPendingException(); - - if (isConnected()) - return true; - // FIXME: Handle blocking/non-blocking mode. - - Selector selector = provider().openSelector(); - register(selector, SelectionKey.OP_CONNECT); - - if (isBlocking()) + InetSocketAddress remote = channel.getPeerAddress(); + if (remote != null) { - selector.select(); // blocking until channel is connected. connectionPending = false; return true; } - - int ready = selector.selectNow(); // non-blocking - if (ready == 1) - { - connectionPending = false; - return true; - } - + + if (!connectionPending) + throw new NoConnectionPendingException(); + return false; } - public boolean isConnected () + public boolean isConnected() { - return socket.isConnected(); + // Wait until finishConnect is called before transitioning to + // connected. + if (connectionPending) + return false; + try + { + InetSocketAddress remote = channel.getPeerAddress(); + return remote != null; + } + catch (IOException ioe) + { + ioe.printStackTrace(System.out); + return false; + } } public boolean isConnectionPending () @@ -216,52 +228,7 @@ public final class SocketChannelImpl extends SocketChannel if (!isConnected()) throw new NotYetConnectedException(); - byte[] data; - int offset = 0; - InputStream input = socket.getInputStream(); - int available = input.available(); - int len = dst.remaining(); - - if ((! isBlocking()) && available == 0) - return 0; - - if (dst.hasArray()) - { - offset = dst.arrayOffset() + dst.position(); - data = dst.array(); - } - else - { - data = new byte [len]; - } - - int readBytes = 0; - boolean completed = false; - - try - { - begin(); - socket.getPlainSocketImpl().setInChannelOperation(true); - readBytes = input.read (data, offset, len); - completed = true; - } - finally - { - end (completed); - socket.getPlainSocketImpl().setInChannelOperation(false); - } - - if (readBytes > 0) - if (dst.hasArray()) - { - dst.position (dst.position() + readBytes); - } - else - { - dst.put (data, offset, readBytes); - } - - return readBytes; + return channel.read(dst); } public long read (ByteBuffer[] dsts, int offset, int length) @@ -275,61 +242,19 @@ public final class SocketChannelImpl extends SocketChannel || (length < 0) || (length > (dsts.length - offset))) throw new IndexOutOfBoundsException(); - - long readBytes = 0; - - for (int index = offset; index < length; index++) - readBytes += read (dsts [index]); - - return readBytes; + + return channel.readScattering(dsts, offset, length); } - public int write (ByteBuffer src) - throws IOException + public int write(ByteBuffer src) throws IOException { if (!isConnected()) throw new NotYetConnectedException(); - - byte[] data; - int offset = 0; - int len = src.remaining(); - - if (!src.hasArray()) - { - data = new byte [len]; - src.get (data, 0, len); - } - else - { - offset = src.arrayOffset() + src.position(); - data = src.array(); - } - OutputStream output = socket.getOutputStream(); - boolean completed = false; - - try - { - begin(); - socket.getPlainSocketImpl().setInChannelOperation(true); - output.write (data, offset, len); - completed = true; - } - finally - { - end (completed); - socket.getPlainSocketImpl().setInChannelOperation(false); - } - - if (src.hasArray()) - { - src.position (src.position() + len); - } - - return len; + return channel.write(src); } - public long write (ByteBuffer[] srcs, int offset, int length) + public long write(ByteBuffer[] srcs, int offset, int length) throws IOException { if (!isConnected()) @@ -340,12 +265,13 @@ public final class SocketChannelImpl extends SocketChannel || (length < 0) || (length > (srcs.length - offset))) throw new IndexOutOfBoundsException(); - - long writtenBytes = 0; - - for (int index = offset; index < length; index++) - writtenBytes += write (srcs [index]); - return writtenBytes; + return channel.writeGathering(srcs, offset, length); + } + + public VMChannel getVMChannel() + { + // XXX security check? + return channel; } } diff --git a/libjava/classpath/gnu/java/nio/SocketChannelSelectionKey.java b/libjava/classpath/gnu/java/nio/SocketChannelSelectionKey.java index 75b4dfd87e5..9ceebdec90f 100644 --- a/libjava/classpath/gnu/java/nio/SocketChannelSelectionKey.java +++ b/libjava/classpath/gnu/java/nio/SocketChannelSelectionKey.java @@ -38,6 +38,7 @@ exception statement from your version. */ package gnu.java.nio; +import java.io.IOException; import java.nio.channels.spi.AbstractSelectableChannel; public final class SocketChannelSelectionKey @@ -49,10 +50,16 @@ public final class SocketChannelSelectionKey super (channel, selector); } + // FIXME don't use file descriptor integers public int getNativeFD() { - NIOSocket socket = - (NIOSocket) ((SocketChannelImpl) ch).socket(); - return socket.getPlainSocketImpl().getNativeFD(); + try + { + return ((SocketChannelImpl) ch).getVMChannel().getState().getNativeFD(); + } + catch (IOException ioe) + { + throw new IllegalStateException(ioe); + } } } diff --git a/libjava/classpath/gnu/java/nio/SocketChannelSelectionKeyImpl.java b/libjava/classpath/gnu/java/nio/SocketChannelSelectionKeyImpl.java index 30fb2dfba44..31a96ed7d88 100644 --- a/libjava/classpath/gnu/java/nio/SocketChannelSelectionKeyImpl.java +++ b/libjava/classpath/gnu/java/nio/SocketChannelSelectionKeyImpl.java @@ -38,6 +38,8 @@ exception statement from your version. */ package gnu.java.nio; +import java.io.IOException; + /** * @author Michael Barker <mike@middlesoft.co.uk> @@ -63,7 +65,14 @@ public class SocketChannelSelectionKeyImpl extends SelectionKeyImpl */ public int getNativeFD() { - return ch.getPlainSocketImpl().getNativeFD(); + try + { + return ch.getVMChannel().getState().getNativeFD(); + } + catch (IOException ioe) + { + return 0; // FIXME + } } } diff --git a/libjava/classpath/gnu/java/nio/VMChannelOwner.java b/libjava/classpath/gnu/java/nio/VMChannelOwner.java new file mode 100644 index 00000000000..363dea2b214 --- /dev/null +++ b/libjava/classpath/gnu/java/nio/VMChannelOwner.java @@ -0,0 +1,57 @@ +/* NativeFD.java -- interface for Channels that have an underlying file descriptor. + 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; + +/** + * This interface is meant to be implemented by any {@link Channel} + * implementation we support that uses a platform-specific {@link VMChannel} + * at their core. This is primarily used by {@link Selector} implementations, + * for easier access to the native state. + * + * @author Casey Marshall (csm@gnu.org) + */ +interface VMChannelOwner +{ + /** + * Return the underlying platform-specific Channel instance. + * + * @return The platform channel object. + */ + VMChannel getVMChannel(); +} diff --git a/libjava/classpath/gnu/java/rmi/server/ActivatableRef.java b/libjava/classpath/gnu/java/rmi/server/ActivatableRef.java index d191c0c1717..e7580bcd79d 100644 --- a/libjava/classpath/gnu/java/rmi/server/ActivatableRef.java +++ b/libjava/classpath/gnu/java/rmi/server/ActivatableRef.java @@ -112,8 +112,10 @@ public class ActivatableRef extends UnicastRef public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - super.readExternal(in); actId = (ActivationID) in.readObject(); + String type = in.readUTF(); + // XXX handle type.equals("") (null reference) + super.readExternal(in); } /** @@ -121,8 +123,10 @@ public class ActivatableRef extends UnicastRef */ public void writeExternal(ObjectOutput out) throws IOException { - super.writeExternal(out); out.writeObject(actId); + // XXX write a "" if the "nested" reference is a null reference + out.writeUTF("UnicastRef2"); + super.writeExternal(out); } /** diff --git a/libjava/classpath/gnu/java/rmi/server/RMIClassLoaderImpl.java b/libjava/classpath/gnu/java/rmi/server/RMIClassLoaderImpl.java index 2e1e7805531..82f0ff69e4e 100644 --- a/libjava/classpath/gnu/java/rmi/server/RMIClassLoaderImpl.java +++ b/libjava/classpath/gnu/java/rmi/server/RMIClassLoaderImpl.java @@ -1,5 +1,5 @@ /* RMIClassLoaderImpl.java -- FIXME: briefly describe file purpose - Copyright (C) 2005 Free Software Foundation, Inc. + Copyright (C) 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,6 +38,7 @@ exception statement from your version. */ package gnu.java.rmi.server; +import java.lang.reflect.Proxy; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; @@ -186,6 +187,7 @@ public class RMIClassLoaderImpl extends RMIClassLoaderSpi { defaultClassLoader = new MyClassLoader (new URL[] { defaultCodebase }, null, defaultAnnotation); + // XXX using getContextClassLoader here *cannot* be right cacheLoaders.put (new CacheKey (defaultAnnotation, Thread.currentThread().getContextClassLoader()), defaultClassLoader); @@ -216,47 +218,53 @@ public class RMIClassLoaderImpl extends RMIClassLoaderSpi ClassLoader defaultLoader) throws MalformedURLException, ClassNotFoundException { - ClassLoader loader; - if (defaultLoader == null) - loader = Thread.currentThread().getContextClassLoader(); - else - loader = defaultLoader; - - //try context class loader first try { - return Class.forName(name, false, loader); + if (defaultLoader != null) + return Class.forName(name, false, defaultLoader); } catch (ClassNotFoundException e) { - // class not found in the local classpath + } + + return Class.forName(name, false, getClassLoader(codeBase)); + } + + public Class loadProxyClass(String codeBase, String[] interfaces, + ClassLoader defaultLoader) + throws MalformedURLException, ClassNotFoundException + { + Class clss[] = new Class[interfaces.length]; + + for (int i = 0; i < interfaces.length; i++) + { + clss[i] = loadClass(codeBase, interfaces[i], defaultLoader); } - if (codeBase.length() == 0) //=="" + // Chain all class loaders (they may differ). + ArrayList loaders = new ArrayList(clss.length); + ClassLoader loader = null; + for (int i = 0; i < clss.length; i++) { - loader = defaultClassLoader; + loader = clss[i].getClassLoader(); + if (! loaders.contains(loader)) + { + loaders.add(0, loader); + } } - else + if (loaders.size() > 1) { - loader = getClassLoader(codeBase); + loader = new CombinedClassLoader(loaders); } - if (loader == null) + try { - //do not throw NullPointerException - throw new ClassNotFoundException ("Could not find class (" + name + - ") at codebase (" + codeBase + ")"); + return Proxy.getProxyClass(loader, clss); + } + catch (IllegalArgumentException e) + { + throw new ClassNotFoundException(null, e); } - - return Class.forName(name, false, loader); - } - - public Class loadProxyClass(String codeBase, String[] interfaces, - ClassLoader defaultLoader) - throws MalformedURLException, ClassNotFoundException - { - // FIXME: Implement this. - return null; } /** @@ -272,6 +280,9 @@ public class RMIClassLoaderImpl extends RMIClassLoaderSpi public ClassLoader getClassLoader(String codebase) throws MalformedURLException { + if (codebase == null || codebase.length() == 0) + return Thread.currentThread().getContextClassLoader(); + ClassLoader loader; CacheKey loaderKey = new CacheKey (codebase, Thread.currentThread().getContextClassLoader()); diff --git a/libjava/classpath/gnu/java/rmi/server/RMIObjectInputStream.java b/libjava/classpath/gnu/java/rmi/server/RMIObjectInputStream.java index e76535447be..75f4f120236 100644 --- a/libjava/classpath/gnu/java/rmi/server/RMIObjectInputStream.java +++ b/libjava/classpath/gnu/java/rmi/server/RMIObjectInputStream.java @@ -1,5 +1,5 @@ /* RMIObjectInputStream.java -- - Copyright (c) 1996, 1997, 1998, 1999, 2002, 2004 + Copyright (c) 1996, 1997, 1998, 1999, 2002, 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,11 +39,11 @@ exception statement from your version. */ package gnu.java.rmi.server; +import gnu.classpath.VMStackWalker; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectStreamClass; -import java.lang.reflect.Proxy; import java.net.MalformedURLException; import java.rmi.server.RMIClassLoader; import java.util.ArrayList; @@ -57,16 +57,14 @@ public RMIObjectInputStream(InputStream strm) throws IOException { } protected Class resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { - String annotation = (String)getAnnotation(); - try { - if(annotation == null) - return (RMIClassLoader.loadClass(desc.getName())); - else - return (RMIClassLoader.loadClass(annotation, desc.getName())); + return RMIClassLoader.loadClass( + (String)getAnnotation(), + desc.getName(), + VMStackWalker.firstNonNullClassLoader()); } - catch (MalformedURLException _) { - throw new ClassNotFoundException(desc.getName()); + catch (MalformedURLException x) { + throw new ClassNotFoundException(desc.getName(), x); } } @@ -81,45 +79,16 @@ protected Object getAnnotation() protected Class resolveProxyClass(String intfs[]) throws IOException, ClassNotFoundException { - String annotation = (String) getAnnotation(); - - Class clss[] = new Class[intfs.length]; - - for (int i = 0; i < intfs.length; i++) - { - if (annotation == null) - clss[i] = RMIClassLoader.loadClass(intfs[i]); - else - clss[i] = RMIClassLoader.loadClass(annotation, intfs[i]); - } - - ClassLoader loader; - - if (clss.length > 0) - { - // Chain all class loaders (they may differ). - ArrayList loaders = new ArrayList(intfs.length); - ClassLoader cx; - for (int i = 0; i < clss.length; i++) - { - cx = clss[i].getClassLoader(); - if (!loaders.contains(cx)) - { - loaders.add(0, cx); - } - } - loader = new CombinedClassLoader(loaders); - } - else - loader = ClassLoader.getSystemClassLoader(); - - try + try { - return Proxy.getProxyClass(loader, clss); + return RMIClassLoader.loadProxyClass( + (String)getAnnotation(), + intfs, + VMStackWalker.firstNonNullClassLoader()); } - catch (IllegalArgumentException e) + catch (MalformedURLException x) { - throw new ClassNotFoundException(null, e); + throw new ClassNotFoundException(null, x); } } diff --git a/libjava/classpath/gnu/java/security/.cvsignore b/libjava/classpath/gnu/java/security/.cvsignore new file mode 100644 index 00000000000..11f6639ebbd --- /dev/null +++ b/libjava/classpath/gnu/java/security/.cvsignore @@ -0,0 +1 @@ +Configuration.java diff --git a/libjava/classpath/gnu/java/security/Engine.java b/libjava/classpath/gnu/java/security/Engine.java index c6271e3f219..44318af8ec7 100644 --- a/libjava/classpath/gnu/java/security/Engine.java +++ b/libjava/classpath/gnu/java/security/Engine.java @@ -79,158 +79,170 @@ public final class Engine /** This class cannot be instantiated. */ private Engine() { } - // Class method. - // ------------------------------------------------------------------------ - /** - * Get the implementation for <i>algorithm</i> for service - * <i>service</i> from <i>provider</i>. The service is e.g. - * "Signature", and the algorithm "DSA". - * - * @param service The service name. + * Return the implementation for <i>algorithm</i> for service <i>service</i> + * from <i>provider</i>. The service is e.g. "Signature", and the algorithm + * "DSA". + * + * @param service The service name. * @param algorithm The name of the algorithm to get. - * @param provider The provider to get the implementation from. - * @return The engine class for the specified algorithm; the object - * returned is typically a subclass of the SPI class for that - * service, but callers should check that this is so. - * @throws NoSuchAlgorithmException If the implementation cannot be - * found or cannot be instantiated. - * @throws InvocationTargetException If the SPI class's constructor - * throws an exception. - * @throws IllegalArgumentException If any of the three arguments are null. + * @param provider The provider to get the implementation from. + * @return The engine class for the specified algorithm; the object returned + * is typically a subclass of the SPI class for that service, but + * callers should check that this is so. + * @throws NoSuchAlgorithmException If the implementation cannot be found or + * cannot be instantiated. + * @throws InvocationTargetException If the SPI class's constructor throws an + * exception. + * @throws IllegalArgumentException If any of the three arguments is null. */ public static Object getInstance(String service, String algorithm, Provider provider) - throws InvocationTargetException, NoSuchAlgorithmException + throws InvocationTargetException, NoSuchAlgorithmException { return getInstance(service, algorithm, provider, NO_ARGS); } /** - * Get the implementation for <i>algorithm</i> for service - * <i>service</i> from <i>provider</i>, passing <i>initArgs</i> to the - * SPI class's constructor (which cannot be null; pass a zero-length - * array if the SPI takes no arguments). The service is e.g. - * "Signature", and the algorithm "DSA". - * - * @param service The service name. + * Return the implementation for <i>algorithm</i> for service <i>service</i> + * from <i>provider</i>, passing <i>initArgs</i> to the SPI class's + * constructor (which cannot be null; pass a zero-length array if the SPI + * takes no arguments). The service is e.g. "Signature", and the algorithm + * "DSA". + * + * @param service The service name. * @param algorithm The name of the algorithm to get. - * @param provider The provider to get the implementation from. - * @param initArgs The arguments to pass to the SPI class's - * constructor (cannot be null). - * @return The engine class for the specified algorithm; the object - * returned is typically a subclass of the SPI class for that - * service, but callers should check that this is so. - * @throws NoSuchAlgorithmException If the implementation cannot be - * found or cannot be instantiated. - * @throws InvocationTargetException If the SPI class's constructor - * throws an exception. - * @throws IllegalArgumentException If any of the four arguments are null. + * @param provider The provider to get the implementation from. + * @param initArgs The arguments to pass to the SPI class's constructor + * (cannot be null). + * @return The engine class for the specified algorithm; the object returned + * is typically a subclass of the SPI class for that service, but + * callers should check that this is so. + * @throws NoSuchAlgorithmException If the implementation cannot be found or + * cannot be instantiated. + * @throws InvocationTargetException If the SPI class's constructor throws an + * exception. + * @throws IllegalArgumentException If any of the four arguments is + * <code>null</code> or if either <code>service</code>, or + * <code>algorithm</code> is an empty string. */ public static Object getInstance(String service, String algorithm, Provider provider, Object[] initArgs) - throws InvocationTargetException, NoSuchAlgorithmException + throws InvocationTargetException, NoSuchAlgorithmException { - if (service != null) - service = service.trim(); + if (service == null) + throw new IllegalArgumentException("service MUST NOT be null"); + service = service.trim(); + if (service.length() == 0) + throw new IllegalArgumentException("service MUST NOT be empty"); + if (algorithm == null) + throw new IllegalArgumentException("algorithm MUST NOT be null"); + algorithm = algorithm.trim(); + if (algorithm.length() == 0) + throw new IllegalArgumentException("algorithm MUST NOT be empty"); + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); + if (initArgs == null) + throw new IllegalArgumentException("Constructor's parameters MUST NOT be null"); - if (algorithm != null) - algorithm = algorithm.trim(); - - if (service == null || service.length() == 0 - || algorithm == null || algorithm.length() == 0 - || provider == null || initArgs == null) - throw new IllegalArgumentException(); - - Enumeration enumer = provider.propertyNames(); String key; String alias; int count = 0; boolean algorithmFound = false; - + StringBuilder sb = new StringBuilder(); while (enumer.hasMoreElements()) { key = (String) enumer.nextElement(); - if (key.equalsIgnoreCase(service + "." + algorithm)) { // remove the service portion from the key algorithm = key.substring(service.length() + 1); - algorithmFound = true; break; - } else if (key.equalsIgnoreCase(ALG_ALIAS + service + "." + algorithm)) { - alias = (String) provider.getProperty(key); - if (! algorithm.equalsIgnoreCase(alias)) // does not refer to itself { algorithm = alias; if (count++ > MAX_ALIASES) - throw new NoSuchAlgorithmException("too many aliases"); - + { + sb.append("Algorithm [").append(algorithm) + .append("] of type [").append(service) + .append("] from provider [").append(provider) + .append("] has too many aliases"); + throw new NoSuchAlgorithmException(sb.toString()); + } // need to reset enumeration to now look for the alias enumer = provider.propertyNames(); } } } - + if (! algorithmFound) { - throw new NoSuchAlgorithmException(algorithm); + sb.append("Algorithm [").append(algorithm).append("] of type [") + .append(service).append("] from provider [") + .append(provider).append("] is not found"); + throw new NoSuchAlgorithmException(sb.toString()); } - - - // Find and instantiate the implementation. + + // Find and instantiate the implementation Class clazz = null; ClassLoader loader = provider.getClass().getClassLoader(); Constructor constructor = null; - String error = algorithm; - + String className = provider.getProperty(service + "." + algorithm); + sb.append("Class [").append(className).append("] for algorithm [") + .append(algorithm).append("] of type [").append(service) + .append("] from provider [").append(provider).append("] "); + Throwable cause = null; try { if (loader != null) - clazz = loader.loadClass(provider.getProperty(service+"."+algorithm)); + clazz = loader.loadClass(className); else - clazz = Class.forName(provider.getProperty(service+"."+algorithm)); + clazz = Class.forName(className); constructor = getCompatibleConstructor(clazz, initArgs); return constructor.newInstance(initArgs); } - catch (ClassNotFoundException cnfe) + catch (ClassNotFoundException x) { - error = "class not found: " + algorithm; + sb.append("cannot not be found"); + cause = x; } - catch (IllegalAccessException iae) + catch (IllegalAccessException x) { - error = "illegal access: " + iae.getMessage(); + sb.append("cannot be accessed"); + cause = x; } - catch (InstantiationException ie) + catch (InstantiationException x) { - error = "instantiation exception: " + ie.getMessage(); + sb.append("cannot be instantiated"); + cause = x; } - catch (ExceptionInInitializerError eiie) + catch (ExceptionInInitializerError x) { - error = "exception in initializer: " + eiie.getMessage(); + sb.append("cannot be initialized"); + cause = x; } - catch (SecurityException se) + catch (SecurityException x) { - error = "security exception: " + se.getMessage(); + sb.append("caused a security violation"); + cause = x; } - catch (NoSuchMethodException nsme) + catch (NoSuchMethodException x) { - error = "no appropriate constructor found"; + sb.append("does not have/expose an appropriate constructor"); + cause = x; } - throw new NoSuchAlgorithmException(error); + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } - // Own methods. - // ------------------------------------------------------------------------ - /** * Find a constructor in the given class that can take the specified * argument list, allowing any of which to be null. diff --git a/libjava/classpath/gnu/java/security/Requires.java b/libjava/classpath/gnu/java/security/Requires.java new file mode 100644 index 00000000000..c820336c01e --- /dev/null +++ b/libjava/classpath/gnu/java/security/Requires.java @@ -0,0 +1,59 @@ +/* Requires.java -- mark methods as requiring permission. + 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.security; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.CLASS; +import java.security.Permission; + +/** + * + * + * @author Casey Marshall (csm@gnu.org) + */ +@Documented @Retention(CLASS) @Target(METHOD) +public @interface Requires +{ + Class<? extends Permission> permissionClass(); + String target(); + String action(); +} diff --git a/libjava/classpath/gnu/java/security/action/GetPropertyAction.java b/libjava/classpath/gnu/java/security/action/GetPropertyAction.java index 2886deb3474..0c8141a4d9b 100644 --- a/libjava/classpath/gnu/java/security/action/GetPropertyAction.java +++ b/libjava/classpath/gnu/java/security/action/GetPropertyAction.java @@ -49,7 +49,7 @@ import java.security.PrivilegedAction; * String port = AccessController.doPrivileged(action); * </code> */ -public class GetPropertyAction implements PrivilegedAction +public class GetPropertyAction implements PrivilegedAction<String> { String name; String value = null; @@ -68,7 +68,7 @@ public class GetPropertyAction implements PrivilegedAction setParameters(propName, defaultValue); } - public Object run() + public String run() { return System.getProperty(name, value); } diff --git a/libjava/classpath/gnu/java/security/action/GetSecurityPropertyAction.java b/libjava/classpath/gnu/java/security/action/GetSecurityPropertyAction.java index 97fa15d03e5..ac928ca336f 100644 --- a/libjava/classpath/gnu/java/security/action/GetSecurityPropertyAction.java +++ b/libjava/classpath/gnu/java/security/action/GetSecurityPropertyAction.java @@ -50,7 +50,7 @@ import java.security.Security; * String passwd = AccessController.doPrivileged(action); * </code> */ -public class GetSecurityPropertyAction implements PrivilegedAction +public class GetSecurityPropertyAction implements PrivilegedAction<String> { private String name; private String value; @@ -83,7 +83,7 @@ public class GetSecurityPropertyAction implements PrivilegedAction return this; } - public Object run() + public String run() { String val = Security.getProperty(name); if (val == null) diff --git a/libjava/classpath/gnu/java/security/key/rsa/RSAKeyPairRawCodec.java b/libjava/classpath/gnu/java/security/key/rsa/RSAKeyPairRawCodec.java index c2650e6a49c..95220236617 100644 --- a/libjava/classpath/gnu/java/security/key/rsa/RSAKeyPairRawCodec.java +++ b/libjava/classpath/gnu/java/security/key/rsa/RSAKeyPairRawCodec.java @@ -81,11 +81,11 @@ public class RSAKeyPairRawCodec * the <code>toByteArray()</code> method on the RSA parameter <code>e</code>. * </li> * </ol> - * + * * @param key the key to encode. * @return the <i>Raw</i> format encoding of the designated key. * @exception IllegalArgumentException if the designated key is not an RSA - * one. + * one. */ public byte[] encodePublicKey(PublicKey key) { diff --git a/libjava/classpath/gnu/java/security/util/ByteBufferOutputStream.java b/libjava/classpath/gnu/java/security/util/ByteBufferOutputStream.java new file mode 100644 index 00000000000..be4d0a98d67 --- /dev/null +++ b/libjava/classpath/gnu/java/security/util/ByteBufferOutputStream.java @@ -0,0 +1,118 @@ +/* ByteBufferOutputStream.java -- output stream with a growable underlying + byte buffer. + 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.security.util; + +import java.io.IOException; +import java.io.OutputStream; + +import java.nio.ByteBuffer; + +/** + * An output stream that writes bytes to a ByteBuffer, which will be resized + * if more space is needed. + * + * @author Casey Marshall (csm@gnu.org) + */ +public class ByteBufferOutputStream extends OutputStream +{ + private ByteBuffer buffer; + + public ByteBufferOutputStream() + { + this(256); + } + + public ByteBufferOutputStream(int initialCapacity) + { + buffer = ByteBuffer.allocate(initialCapacity); + } + + /* (non-Javadoc) + * @see java.io.OutputStream#write(int) + */ + public @Override synchronized void write(int b) throws IOException + { + if (!buffer.hasRemaining()) + growBuffer(); + buffer.put((byte) b); + } + + public @Override synchronized void write(byte[] b, int offset, int length) + { + if (buffer.remaining() < length) + growBuffer(); + buffer.put(b, offset, length); + } + + public @Override void write(byte[] b) + { + write(b, 0, b.length); + } + + /** + * Get the current state of the buffer. The returned buffer will have + * its position set to zero, its capacity set to the current limit, + * and its limit set to its capacity. + * + * @return The buffer. + */ + public ByteBuffer buffer() + { + return ((ByteBuffer) buffer.duplicate().flip()).slice(); + } + + public String toString() + { + return super.toString() + " [ buffer: " + buffer + " ]"; + } + + private void growBuffer() + { + int newCapacity = buffer.capacity(); + if (newCapacity < 16384) // If the buffer isn't huge yet, double its size + newCapacity = newCapacity << 1; + else // Otherwize, increment by a bit. + newCapacity += 4096; + ByteBuffer newBuffer = ByteBuffer.allocate(newCapacity); + buffer.flip(); + newBuffer.put(buffer); + buffer = newBuffer; + } +} diff --git a/libjava/classpath/gnu/java/util/DoubleEnumeration.java b/libjava/classpath/gnu/java/util/DoubleEnumeration.java index 1fc37f8e0f8..94efb923d2a 100644 --- a/libjava/classpath/gnu/java/util/DoubleEnumeration.java +++ b/libjava/classpath/gnu/java/util/DoubleEnumeration.java @@ -1,5 +1,5 @@ /* gnu.java.util.DoubleEnumeration - Copyright (C) 1998, 1999, 2001 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2001, 2004 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -63,7 +63,7 @@ import java.util.NoSuchElementException; * @author Jochen Hoenicke * @author Mark Wielaard (mark@klomp.org) */ -public class DoubleEnumeration implements Enumeration +public class DoubleEnumeration<T> implements Enumeration<T> { /** * This is true as long as one of the enumerations has more @@ -82,17 +82,17 @@ public class DoubleEnumeration implements Enumeration /** * The first enumeration. */ - private Enumeration e1; + private Enumeration<T> e1; /** * The second enumeration. */ - private Enumeration e2; + private Enumeration<T> e2; /** * Creates a new Enumeration combining the given two enumerations. * The enumerations mustn't be accessed by other classes. */ - public DoubleEnumeration(Enumeration e1, Enumeration e2) + public DoubleEnumeration(Enumeration<T> e1, Enumeration<T> e2) { this.e1 = e1; this.e2 = e2; @@ -126,7 +126,7 @@ public class DoubleEnumeration implements Enumeration * element of the second enumeration. If both enumeration don't have * any elements it throws a <code>NoSuchElementException</code>. */ - public Object nextElement() + public T nextElement() { if (!hasMoreElements()) throw new NoSuchElementException(); diff --git a/libjava/classpath/gnu/java/util/prefs/GConfBasedPreferences.java b/libjava/classpath/gnu/java/util/prefs/GConfBasedPreferences.java index 5702751cf5c..a7e2322b524 100644 --- a/libjava/classpath/gnu/java/util/prefs/GConfBasedPreferences.java +++ b/libjava/classpath/gnu/java/util/prefs/GConfBasedPreferences.java @@ -72,7 +72,6 @@ import java.util.prefs.BackingStoreException; * <br /> * * @author Mario Torre <neugens@limasoftware.net> - * @version 1.0.1 */ public class GConfBasedPreferences extends AbstractPreferences @@ -136,12 +135,20 @@ public class GConfBasedPreferences absolutePath = absolutePath.substring(0, absolutePath.length() - 1); } + // strip invalid characters + // please, note that all names are unescaped into the native peer + int index = absolutePath.lastIndexOf('/'); + if (index > -1) + { + absolutePath = absolutePath.substring(0, index + 1); + absolutePath = absolutePath + GConfNativePeer.escapeString(name); + } + this.node = this.getRealRoot(isUser) + absolutePath; boolean nodeExist = backend.nodeExist(this.node); this.newNode = !nodeExist; - backend.startWatchingNode(this.node); } /** @@ -156,7 +163,15 @@ public class GConfBasedPreferences // we don't check anything here, if the node is a new node this will be // detected in the constructor, so we simply return a new reference to // the requested node. - return new GConfBasedPreferences(this, name, this.isUser); + + GConfBasedPreferences preferenceNode + = new GConfBasedPreferences(this, name, this.isUser); + + // register the node for to GConf so that it can listen + // events outside the scope of the application + backend.startWatchingNode(this.node); + + return preferenceNode; } /** @@ -365,6 +380,10 @@ public class GConfBasedPreferences { String nodeName = ""; + // strip key + // please, note that all names are unescaped into the native peer + key = GConfNativePeer.escapeString(key); + if (this.node.endsWith("/")) { nodeName = this.node + key; @@ -373,7 +392,7 @@ public class GConfBasedPreferences { nodeName = this.node + "/" + key; } - + return nodeName; } diff --git a/libjava/classpath/gnu/java/util/prefs/gconf/GConfNativePeer.java b/libjava/classpath/gnu/java/util/prefs/gconf/GConfNativePeer.java index f1cb6278767..6049863e916 100644 --- a/libjava/classpath/gnu/java/util/prefs/gconf/GConfNativePeer.java +++ b/libjava/classpath/gnu/java/util/prefs/gconf/GConfNativePeer.java @@ -45,7 +45,6 @@ import java.util.prefs.BackingStoreException; * Native peer for GConf based preference backend. * * @author Mario Torre <neugens@limasoftware.net> - * @version 1.0.1 */ public final class GConfNativePeer { @@ -150,7 +149,7 @@ public final class GConfNativePeer */ public List getKeys(String node) throws BackingStoreException { - return gconf_client_gconf_client_all_keys(node); + return gconf_client_all_keys(node); } /** @@ -162,10 +161,26 @@ public final class GConfNativePeer */ public List getChildrenNodes(String node) throws BackingStoreException { - return gconf_client_gconf_client_all_nodes(node); + return gconf_client_all_nodes(node); } /** + * Escape the given string so the it is a valid GConf name. + */ + public static String escapeString(String plain) + { + return gconf_escape_key(plain); + } + + /** + * Unescape a string escaped with {@link #escapeString}. + */ + public static String unescapeString(String escaped) + { + return gconf_unescape_key(escaped); + } + + /** * Suggest to the backend GConf daemon to synch with the database. */ public void suggestSync() throws BackingStoreException @@ -270,8 +285,9 @@ public final class GConfNativePeer * Suggest to the GConf native peer a sync with the database. * */ - native static final protected void gconf_client_suggest_sync(); - + native static final protected void gconf_client_suggest_sync() + throws BackingStoreException; + /** * Returns a list of all nodes under the given node. * @@ -279,8 +295,9 @@ public final class GConfNativePeer * @return A list of nodes under the given source node. */ native - static final protected List gconf_client_gconf_client_all_nodes(String node); - + static final protected List gconf_client_all_nodes(String node) + throws BackingStoreException; + /** * Returns a list of all keys stored in the given node. * @@ -288,8 +305,28 @@ public final class GConfNativePeer * @return A list of all keys stored in the given node. */ native - static final protected List gconf_client_gconf_client_all_keys(String node); + static final protected List gconf_client_all_keys(String node) + throws BackingStoreException; + /** + * Escape the input String so that it's a valid element for GConf. + * + * @param plain the String to escape. + * @return An escaped String for use with GConf. + */ + native + static final protected String gconf_escape_key(String plain); + + /** + * Converts a string escaped with gconf_escape_key back into its + * original form. + * + * @param escaped key as returned by gconf_escape_key + * @return An unescaped key. + */ + native + static final protected String gconf_unescape_key(String escaped); + static { System.loadLibrary("gconfpeer"); diff --git a/libjava/classpath/gnu/java/util/regex/CharIndexed.java b/libjava/classpath/gnu/java/util/regex/CharIndexed.java index 6cd857e3bc0..27e07b2f8ff 100644 --- a/libjava/classpath/gnu/java/util/regex/CharIndexed.java +++ b/libjava/classpath/gnu/java/util/regex/CharIndexed.java @@ -77,6 +77,13 @@ public interface CharIndexed { boolean move(int index); /** + * Shifts the input buffer by a given number of positions. Returns + * true if the new cursor position is valid or cursor position is at + * the end of input. + */ + boolean move1(int index); // I cannot think of a better name for this. + + /** * Returns true if the most recent move() operation placed the cursor * position at a valid position in the input. */ @@ -105,6 +112,16 @@ public interface CharIndexed { REMatch getLastMatch(); /** + * Sets the information used for hitEnd(). + */ + void setHitEnd(REMatch match); + + /** + * Returns whether the matcher has hit the end of input. + */ + boolean hitEnd(); + + /** * Returns the anchor. */ int getAnchor(); diff --git a/libjava/classpath/gnu/java/util/regex/CharIndexedCharSequence.java b/libjava/classpath/gnu/java/util/regex/CharIndexedCharSequence.java index 2eb753b0f5c..8a0578eb80f 100644 --- a/libjava/classpath/gnu/java/util/regex/CharIndexedCharSequence.java +++ b/libjava/classpath/gnu/java/util/regex/CharIndexedCharSequence.java @@ -62,6 +62,10 @@ class CharIndexedCharSequence implements CharIndexed, Serializable { return ((anchor += index) < len); } + public boolean move1(int index) { + return ((anchor += index) <= len); + } + public CharIndexed lookBehind(int index, int length) { if (length > (anchor + index)) length = anchor + index; return new CharIndexedCharSequence(s, anchor + index - length); @@ -77,6 +81,15 @@ class CharIndexedCharSequence implements CharIndexed, Serializable { lastMatch.anchor = anchor; } public REMatch getLastMatch() { return lastMatch; } + + private int rightmostTriedPosition = 0; + public void setHitEnd(REMatch match) { + int pos = anchor + match.index; + if (pos > rightmostTriedPosition) rightmostTriedPosition = pos; + } + public boolean hitEnd() { return rightmostTriedPosition >= len; } + public int getAnchor() { return anchor; } public void setAnchor(int anchor) { this.anchor = anchor; } + } diff --git a/libjava/classpath/gnu/java/util/regex/CharIndexedInputStream.java b/libjava/classpath/gnu/java/util/regex/CharIndexedInputStream.java index 77cd1abd5cc..844fada51fc 100644 --- a/libjava/classpath/gnu/java/util/regex/CharIndexedInputStream.java +++ b/libjava/classpath/gnu/java/util/regex/CharIndexedInputStream.java @@ -166,6 +166,16 @@ class CharIndexedInputStream implements CharIndexed { "difficult to support getLastMatch for an input stream"); } + public void setHitEnd(REMatch match) { + throw new UnsupportedOperationException( + "difficult to support setHitEnd for an input stream"); + } + + public boolean hitEnd() { + throw new UnsupportedOperationException( + "difficult to support hitEnd for an input stream"); + } + public int getAnchor() { throw new UnsupportedOperationException( "difficult to support getAnchor for an input stream"); @@ -176,6 +186,10 @@ class CharIndexedInputStream implements CharIndexed { "difficult to support setAnchor for an input stream"); } + public boolean move1(int index) { + throw new UnsupportedOperationException( + "difficult to support move1 for an input stream"); + } } diff --git a/libjava/classpath/gnu/java/util/regex/RE.java b/libjava/classpath/gnu/java/util/regex/RE.java index 1aab3b781a2..09ff74b908f 100644 --- a/libjava/classpath/gnu/java/util/regex/RE.java +++ b/libjava/classpath/gnu/java/util/regex/RE.java @@ -130,7 +130,11 @@ public class RE extends REToken { private static final String VERSION = "1.1.5-dev"; // The localized strings are kept in a separate file - private static ResourceBundle messages = PropertyResourceBundle.getBundle("gnu/java/util/regex/MessagesBundle", Locale.getDefault()); + // Used by getLocalizedMessage(). + private static ResourceBundle messages; + + // Name of the bundle that contains the localized messages. + private static final String bundle = "gnu/java/util/regex/MessagesBundle"; // These are, respectively, the first and last tokens in our linked list // If there is only one token, firstToken == lastToken @@ -252,6 +256,13 @@ public class RE extends REToken { */ public static final int REG_ICASE_USASCII = 0x0800; + /** + * Execution flag. + * Do not move the position at which the search begins. If not set, + * the starting position will be moved until a match is found. + */ + public static final int REG_FIX_STARTING_POSITION = 0x1000; + /** Returns a string representing the version of the gnu.regexp package. */ public static final String version() { return VERSION; @@ -259,6 +270,8 @@ public class RE extends REToken { // Retrieves a message from the ResourceBundle static final String getLocalizedMessage(String key) { + if (messages == null) + messages = PropertyResourceBundle.getBundle(bundle, Locale.getDefault()); return messages.getString(key); } @@ -1643,6 +1656,7 @@ public class RE extends REToken { /* Implements abstract method REToken.match() */ boolean match(CharIndexed input, REMatch mymatch) { + input.setHitEnd(mymatch); if (firstToken == null) { return next(input, mymatch); } @@ -1720,15 +1734,23 @@ public class RE extends REToken { REMatch getMatchImpl(CharIndexed input, int anchor, int eflags, StringBuffer buffer) { boolean tryEntireMatch = ((eflags & REG_TRY_ENTIRE_MATCH) != 0); + boolean doMove = ((eflags & REG_FIX_STARTING_POSITION) == 0); RE re = (tryEntireMatch ? (RE) this.clone() : this); if (tryEntireMatch) { - re.chain(new RETokenEnd(0, null)); + RETokenEnd reEnd = new RETokenEnd(0, null); + reEnd.setFake(true); + re.chain(reEnd); } // Create a new REMatch to hold results REMatch mymatch = new REMatch(numSubs, anchor, eflags); do { + /* The following potimization is commented out because + the matching should be tried even if the length of + input is obviously too short in order that + java.util.regex.Matcher#hitEnd() may work correctly. // Optimization: check if anchor + minimumLength > length if (minimumLength == 0 || input.charAt(minimumLength-1) != CharIndexed.OUT_OF_BOUNDS) { + */ if (re.match(input, mymatch)) { REMatch best = mymatch; // We assume that the match that coms first is the best. @@ -1749,13 +1771,17 @@ public class RE extends REToken { input.setLastMatch(best); return best; } - } + /* End of the optimization commented out + } + */ mymatch.clear(++anchor); // Append character to buffer if needed if (buffer != null && input.charAt(0) != CharIndexed.OUT_OF_BOUNDS) { buffer.append(input.charAt(0)); } - } while (input.move(1)); + // java.util.regex.Matcher#hitEnd() requires that the search should + // be tried at the end of input, so we use move1(1) instead of move(1) + } while (doMove && input.move1(1)); // Special handling at end of input for e.g. "$" if (minimumLength == 0) { diff --git a/libjava/classpath/gnu/java/util/regex/REMatch.java b/libjava/classpath/gnu/java/util/regex/REMatch.java index 3ff5ad794b8..d8994829323 100644 --- a/libjava/classpath/gnu/java/util/regex/REMatch.java +++ b/libjava/classpath/gnu/java/util/regex/REMatch.java @@ -307,12 +307,12 @@ public final class REMatch implements Serializable, Cloneable { } /* The following are used for debugging purpose - static String d(REMatch m) { + public static String d(REMatch m) { if (m == null) return "null"; else return "[" + m.index + "]"; } - String substringUptoIndex(CharIndexed input) { + public String substringUptoIndex(CharIndexed input) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < index; i++) { sb.append(input.charAt(i)); diff --git a/libjava/classpath/gnu/java/util/regex/RESyntax.java b/libjava/classpath/gnu/java/util/regex/RESyntax.java index b66b32f5878..db11e2db450 100644 --- a/libjava/classpath/gnu/java/util/regex/RESyntax.java +++ b/libjava/classpath/gnu/java/util/regex/RESyntax.java @@ -54,8 +54,6 @@ import java.util.BitSet; public final class RESyntax implements Serializable { static final String DEFAULT_LINE_SEPARATOR = System.getProperty("line.separator"); - private static final String SYNTAX_IS_FINAL = RE.getLocalizedMessage("syntax.final"); - private BitSet bits; // true for the constant defined syntaxes @@ -513,7 +511,8 @@ public final class RESyntax implements Serializable { * @return a reference to this object for easy chaining. */ public RESyntax set(int index) { - if (isFinal) throw new IllegalAccessError(SYNTAX_IS_FINAL); + if (isFinal) + throw new IllegalAccessError(RE.getLocalizedMessage("syntax.final")); bits.set(index); return this; } @@ -525,7 +524,8 @@ public final class RESyntax implements Serializable { * @return a reference to this object for easy chaining. */ public RESyntax clear(int index) { - if (isFinal) throw new IllegalAccessError(SYNTAX_IS_FINAL); + if (isFinal) + throw new IllegalAccessError(RE.getLocalizedMessage("syntax.final")); bits.clear(index); return this; } @@ -548,7 +548,8 @@ public final class RESyntax implements Serializable { * @return this object for convenient chaining */ public RESyntax setLineSeparator(String aSeparator) { - if (isFinal) throw new IllegalAccessError(SYNTAX_IS_FINAL); + if (isFinal) + throw new IllegalAccessError(RE.getLocalizedMessage("syntax.final")); lineSeparator = aSeparator; return this; } diff --git a/libjava/classpath/gnu/java/util/regex/REToken.java b/libjava/classpath/gnu/java/util/regex/REToken.java index 155c01878e8..9affd4ee3c9 100644 --- a/libjava/classpath/gnu/java/util/regex/REToken.java +++ b/libjava/classpath/gnu/java/util/regex/REToken.java @@ -72,6 +72,16 @@ abstract class REToken implements Serializable, Cloneable { /** Returns true if the match succeeded, false if it failed. */ boolean match(CharIndexed input, REMatch mymatch) { + return match(input, mymatch, false); + } + boolean matchFake(CharIndexed input, REMatch mymatch) { + return match(input, mymatch, true); + } + + private boolean match(CharIndexed input, REMatch mymatch, boolean fake) { + if (!fake) { + setHitEnd(input, mymatch); + } REMatch m = matchThis(input, mymatch); if (m == null) return false; if (next(input, m)) { @@ -81,6 +91,11 @@ abstract class REToken implements Serializable, Cloneable { return false; } + /** Sets whether the matching occurs at the end of input */ + void setHitEnd(CharIndexed input, REMatch mymatch) { + input.setHitEnd(mymatch); + } + /** Returns true if the match succeeded, false if it failed. * The matching is done against this REToken only. Chained * tokens are not checked. diff --git a/libjava/classpath/gnu/java/util/regex/RETokenChar.java b/libjava/classpath/gnu/java/util/regex/RETokenChar.java index 92d3efcf85b..b70e6b1d843 100644 --- a/libjava/classpath/gnu/java/util/regex/RETokenChar.java +++ b/libjava/classpath/gnu/java/util/regex/RETokenChar.java @@ -58,15 +58,20 @@ final class RETokenChar extends REToken { } REMatch matchThis(CharIndexed input, REMatch mymatch) { - int z = ch.length; if (matchOneString(input, mymatch.index)) { - mymatch.index += z; + mymatch.index += matchedLength; return mymatch; } + // java.util.regex.Matcher#hitEnd() requires that the length of + // partial match be counted. + mymatch.index += matchedLength; + input.setHitEnd(mymatch); return null; } - boolean matchOneString(CharIndexed input, int index) { + private int matchedLength; + private boolean matchOneString(CharIndexed input, int index) { + matchedLength = 0; int z = ch.length; char c; for (int i=0; i<z; i++) { @@ -74,6 +79,7 @@ final class RETokenChar extends REToken { if (! charEquals(c, ch[i])) { return false; } + ++matchedLength; } return true; } diff --git a/libjava/classpath/gnu/java/util/regex/RETokenEnd.java b/libjava/classpath/gnu/java/util/regex/RETokenEnd.java index 00efdb6a712..294e32085b5 100644 --- a/libjava/classpath/gnu/java/util/regex/RETokenEnd.java +++ b/libjava/classpath/gnu/java/util/regex/RETokenEnd.java @@ -45,6 +45,12 @@ final class RETokenEnd extends REToken { private String newline; private boolean check_java_line_terminators; + /** + * Indicates whether this token is a real one generated at compile time, + * or a fake one temporarily added by RE#getMatchImpl. + */ + private boolean fake = false; + RETokenEnd(int subIndex,String newline) { super(subIndex); this.newline = newline; @@ -57,10 +63,19 @@ final class RETokenEnd extends REToken { this.check_java_line_terminators = b; } + void setFake(boolean fake) { + this.fake = fake; + } + int getMaximumLength() { return 0; } + boolean match(CharIndexed input, REMatch mymatch) { + if (!fake) return super.match(input, mymatch); + return super.matchFake(input, mymatch); + } + REMatch matchThis(CharIndexed input, REMatch mymatch) { char ch = input.charAt(mymatch.index); if (ch == CharIndexed.OUT_OF_BOUNDS) diff --git a/libjava/classpath/gnu/java/util/regex/RETokenEndSub.java b/libjava/classpath/gnu/java/util/regex/RETokenEndSub.java index 57a146d03b9..b3a28a3e82e 100644 --- a/libjava/classpath/gnu/java/util/regex/RETokenEndSub.java +++ b/libjava/classpath/gnu/java/util/regex/RETokenEndSub.java @@ -58,6 +58,10 @@ final class RETokenEndSub extends REToken { return super.findMatch(input, mymatch); } + void setHitEnd(CharIndexed input, REMatch mymatch) { + // Do nothing + } + void dump(StringBuffer os) { // handled by RE // But add something for debugging. diff --git a/libjava/classpath/gnu/java/util/regex/RETokenNamedProperty.java b/libjava/classpath/gnu/java/util/regex/RETokenNamedProperty.java index a286c5be8c6..aec27583a58 100644 --- a/libjava/classpath/gnu/java/util/regex/RETokenNamedProperty.java +++ b/libjava/classpath/gnu/java/util/regex/RETokenNamedProperty.java @@ -260,6 +260,14 @@ final class RETokenNamedProperty extends REToken { return new UnicodeCategoryHandler(Character.UNASSIGNED); if (name.equals("Lu")) return new UnicodeCategoryHandler(Character.UPPERCASE_LETTER); + if (name.equals("all")) + return new Handler() + { + public boolean includes(char c) + { + return true; + } + }; throw new REException("unsupported name " + name, REException.REG_ESCAPE, 0); } diff --git a/libjava/classpath/gnu/java/util/regex/RETokenOneOf.java b/libjava/classpath/gnu/java/util/regex/RETokenOneOf.java index bccc783118a..239c2201ca7 100644 --- a/libjava/classpath/gnu/java/util/regex/RETokenOneOf.java +++ b/libjava/classpath/gnu/java/util/regex/RETokenOneOf.java @@ -120,6 +120,7 @@ final class RETokenOneOf extends REToken { } boolean match(CharIndexed input, REMatch mymatch) { + setHitEnd(input, mymatch); if (matchesOneChar) return matchOneChar(input, mymatch); else return matchOneRE(input, mymatch); } diff --git a/libjava/classpath/gnu/java/util/regex/RETokenRepeated.java b/libjava/classpath/gnu/java/util/regex/RETokenRepeated.java index 531c4a31124..7f5e5626f1e 100644 --- a/libjava/classpath/gnu/java/util/regex/RETokenRepeated.java +++ b/libjava/classpath/gnu/java/util/regex/RETokenRepeated.java @@ -38,8 +38,7 @@ exception statement from your version. */ package gnu.java.util.regex; -// import java.util.Vector; -// import java.util.Stack; +import java.util.ArrayList; final class RETokenRepeated extends REToken { private REToken token; @@ -168,19 +167,63 @@ final class RETokenRepeated extends REToken { } } + private static class FindMatchControlStack extends ArrayList { + private void push(FindMatchControl control) { + add(control); + } + private FindMatchControl pop() { + return (FindMatchControl)remove(size()-1); + } + private boolean empty() { + return isEmpty(); + } + } + + private static class FindMatchControl { + DoablesFinder finder; + FindMatchControl(DoablesFinder finder) { + this.finder = finder; + } + } + private REMatch findMatch(BacktrackStack stack) { - // Avoid using recursive calls. + return findMatch(stack, new FindMatchControlStack()); + } + + private REMatch findMatch(BacktrackStack stack, + FindMatchControlStack controlStack) { + REMatch result = null; + StackedInfo si = null; + CharIndexed input = null; + int numRepeats = 0; + REMatch mymatch = null; + int[] visited = null; + DoablesFinder finder = null; + + // Avoid using recursive calls because a match can be very long. + + // This is the first entry point of this method. + // If you want to call this method recursively and you need the + // result returned, save necessary information in a FindMatchControl + // object and push it to controlStack, then continue from this point. + // You can check the result after exiting MAIN_LOOP. + MAIN_LOOP0: + while (true) { + + // This is the second entry point of this method. + // If you want to call this method recursively but you do not need the + // result returned, just continue from this point. MAIN_LOOP: while (true) { - if (stack.empty()) return null; - StackedInfo si = (StackedInfo)(stack.peek()); - CharIndexed input = si.input; - int numRepeats = si.numRepeats; - REMatch mymatch = si.match; - int[] visited = si.visited; - DoablesFinder finder = si.finder; - + if (stack.empty()) break MAIN_LOOP; + si = (StackedInfo)(stack.peek()); + input = si.input; + numRepeats = si.numRepeats; + mymatch = si.match; + visited = si.visited; + finder = si.finder; + if (mymatch.backtrackStack == null) mymatch.backtrackStack = new BacktrackStack(); @@ -192,12 +235,13 @@ final class RETokenRepeated extends REToken { m1.backtrackStack.push(new BacktrackStack.Backtrack( this, input, mymatch, stack)); } - return m1; + result = m1; + break MAIN_LOOP; } if (stingy) { continue MAIN_LOOP; } - return null; + break MAIN_LOOP; } if (finder == null) { @@ -238,7 +282,8 @@ final class RETokenRepeated extends REToken { m1.backtrackStack.push(new BacktrackStack.Backtrack( this, input, mymatch, stack)); } - return m1; + result = m1; + break MAIN_LOOP; } else { continue MAIN_LOOP; @@ -247,8 +292,82 @@ final class RETokenRepeated extends REToken { visited = addVisited(mymatch.index, visited); + TryAnotherResult taresult = tryAnother(stack, input, mymatch, numRepeats, finder, visited); + visited = taresult.visited; + switch (taresult.status) { + case TryAnotherResult.TRY_FURTHER: + controlStack.push(new FindMatchControl( + finder)); + continue MAIN_LOOP0; + case TryAnotherResult.RESULT_FOUND: + result = taresult.result; + break MAIN_LOOP; + } + + if (!stack.empty()) { + stack.pop(); + } + if (possessive) { + stack.clear(); + } + REMatch m1 = matchRest(input, mymatch); + if (m1 != null) { + if (! stack.empty()) { + m1.backtrackStack.push(new BacktrackStack.Backtrack( + this, input, mymatch, stack)); + } + result = m1; + break MAIN_LOOP; + } + + } // MAIN_LOOP + + if (controlStack.empty()) return result; + FindMatchControl control = controlStack.pop(); + if (possessive) { + return result; + } + if (result != null) { + result.backtrackStack.push(new BacktrackStack.Backtrack( + this, input, mymatch, stack)); + return result; + } + + finder = control.finder; + + TryAnotherResult taresult = tryAnother(stack, input, mymatch, numRepeats, finder, visited); + visited = taresult.visited; + switch (taresult.status) { + case TryAnotherResult.TRY_FURTHER: + controlStack.push(new FindMatchControl( + finder)); + continue MAIN_LOOP0; + case TryAnotherResult.RESULT_FOUND: + return taresult.result; + } + continue MAIN_LOOP0; + + } // MAIN_LOOP0 + } + + private static class TryAnotherResult { + REMatch result; + int status; + static final int RESULT_FOUND = 1; + static final int TRY_FURTHER = 2; + static final int NOTHING_FOUND = 3; + int[] visited; + } + + private TryAnotherResult tryAnother(BacktrackStack stack, + CharIndexed input, REMatch mymatch, int numRepeats, + DoablesFinder finder, int[] visited) { + + TryAnotherResult taresult = new TryAnotherResult(); + taresult.visited = visited; + DO_THIS: - do { + { boolean emptyMatchFound = false; @@ -263,61 +382,45 @@ final class RETokenRepeated extends REToken { if (!emptyMatchFound) { int n = doable.index; - if (! visitedContains(n, visited)) { - visited = addVisited(n, visited); - } - else { + if (visitedContains(n, visited)) { continue DO_ONE_DOABLE; } + visited = addVisited(n, visited); stack.push(new StackedInfo( - input, numRepeats + 1, doable, visited, null)); - REMatch m1 = findMatch(stack); - if (possessive) { - return m1; - } - if (m1 != null) { - m1.backtrackStack.push(new BacktrackStack.Backtrack( - this, input, mymatch, stack)); - return m1; - } + input, numRepeats + 1, doable, visited, null)); + taresult.visited = visited; + taresult.status = TryAnotherResult.TRY_FURTHER; + return taresult; } else { REMatch m1 = matchRest(input, doable); if (possessive) { - return m1; + taresult.result = m1; + taresult.status = TryAnotherResult.RESULT_FOUND; + return taresult; } if (m1 != null) { if (! stack.empty()) { m1.backtrackStack.push(new BacktrackStack.Backtrack( this, input, mymatch, stack)); - } - return m1; + } + taresult.result = m1; + taresult.status = TryAnotherResult.RESULT_FOUND; + return taresult; } } } // DO_ONE_DOABLE - } while (false); // DO_THIS only once; + } // DO_THIS - if (!stack.empty()) { - stack.pop(); - } - if (possessive) { - stack.clear(); - } - REMatch m1 = matchRest(input, mymatch); - if (m1 != null) { - if (! stack.empty()) { - m1.backtrackStack.push(new BacktrackStack.Backtrack( - this, input, mymatch, stack)); - } - return m1; - } + taresult.status = TryAnotherResult.NOTHING_FOUND; + return taresult; - } // MAIN_LOOP } boolean match(CharIndexed input, REMatch mymatch) { + setHitEnd(input, mymatch); REMatch m1 = findMatch(input, mymatch); if (m1 != null) { mymatch.assignFrom(m1); |