Merged gcj-eclipse branch to trunk.

From-SVN: r120621
This commit is contained in:
Tom Tromey
2007-01-09 19:58:05 +00:00
parent c648dedbde
commit 97b8365caf
17478 changed files with 606493 additions and 100744 deletions
@@ -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;
}
}
@@ -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.
}
@@ -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();
}
}
@@ -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();
@@ -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)
@@ -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;
}
@@ -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;
}
@@ -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.
}
}
@@ -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);
}
}
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -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);
}
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -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;
@@ -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
@@ -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);
}
@@ -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,
@@ -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.
@@ -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;
@@ -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>
*
@@ -143,6 +160,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
*/
@@ -183,11 +206,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;
}
}
@@ -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;
}
@@ -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;
}
@@ -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);
@@ -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);
}
@@ -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);
}
public void print(Graphics graphics) {}
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);
}
public void requestFocus() {}
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 print(Graphics graphics)
{
// Nothing to do here for lightweights.
}
public void setBackground(Color color) {}
public void repaint(long tm, int x, int y, int width, int height)
{
// Nothing to do here for lightweights.
}
public void setBounds(int x, int y, int width, int height) {}
public void requestFocus()
{
// Nothing to do here for lightweights.
}
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)
{
// Nothing to do here for lightweights.
}
public void setBackground(Color color)
{
// Nothing to do here for lightweights.
}
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 setEventMask(long eventMask) {}
public void setFont(Font font) {}
public void setForeground(Color color) {}
public void setVisible(boolean visible) {}
public void show() {}
public ColorModel getColorModel ()
public void setEnabled(boolean enabled)
{
return comp.getColorModel ();
// Nothing to do here for lightweights.
}
public void setEventMask(long eventMask)
{
// Nothing to do here for lightweights.
}
public void setFont(Font font)
{
// Nothing to do here for lightweights.
}
public void setForeground(Color color)
{
// Nothing to do here for lightweights.
}
public void setVisible(boolean visible)
{
// Nothing to do here for lightweights.
}
public void show()
{
// Nothing to do here for lightweights.
}
public ColorModel 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.
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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();
}
}
@@ -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);
@@ -189,6 +195,18 @@ public abstract class CairoGraphics2D extends Graphics2D
static ColorModel argb32 = new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF,
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.
*/
@@ -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,
@@ -440,11 +473,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).
*
@@ -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 shifted(double coord, boolean doShift)
private double shiftX(double coord, boolean doShift)
{
if (doShift)
return Math.floor(coord) + 0.5;
{
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 shiftY(double coord, boolean doShift)
{
if (doShift)
{
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;
}
}
}
@@ -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;
// 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 nativeModel = 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});
}
}
@@ -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;
}
}
@@ -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,45 +481,6 @@ 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)
{
lock();
try
{
super.drawLine(x1, y1, x2, y2);
}
finally
{
unlock();
}
}
public void drawRect(int x, int y, int width, int height)
{
lock();
try
{
super.drawRect(x, y, width, height);
}
finally
{
unlock();
}
}
public void fillRect(int x, int y, int width, int height)
{
lock();
try
{
super.fillRect(x, y, width, height);
}
finally
{
unlock();
}
}
public void setClip(Shape s)
{
lock();
@@ -431,5 +494,99 @@ public class ComponentGraphics extends CairoGraphics2D
}
}
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();
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;
}
private void createBuffer()
{
if (buffer == null)
{
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());
}
else
{
Graphics2D g2d = ((Graphics2D)buffer.getGraphics());
g2d.setBackground(new Color(0,0,0,0));
g2d.clearRect(0, 0, buffer.getWidth(), buffer.getHeight());
}
if (componentBuffer == null)
{
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;
}
}
@@ -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;
}
}
@@ -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];
}
}
@@ -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 );
}
}
@@ -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 );
}
@@ -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);
@@ -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());
// 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;
}
awtComponent.paint(g);
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();
// 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.setClip(event.getUpdateRect());
awtComponent.update(g);
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 ()
@@ -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)
@@ -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;
}
}
}
}
@@ -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();
}
}
}
@@ -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
@@ -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});
}
}
@@ -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)
{
@@ -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;
}
}
@@ -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();
}
}
@@ -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;
}
}
@@ -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);
}
}
@@ -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();
}
@@ -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);
@@ -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
@@ -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)
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 peerPaintComponent(Graphics g)
{
// Paint the actual Swing component if this peer has one.
if (swingComponent != null)
swingComponent.getJComponent().paint(g);
}
@@ -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.
@@ -56,14 +62,54 @@ public class SwingContainerPeer
implements ContainerPeer
{
/**
* 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)
{
init(awtCont, null);
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)
{
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;
}
}
@@ -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);
}
@@ -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;
}
}
}
@@ -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);
}
}
@@ -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)
{
@@ -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);
}
}
@@ -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);
}
}
@@ -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.
}
@@ -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);
}
}
@@ -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:
@@ -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");
}
}
@@ -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;
}
}
@@ -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
@@ -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,
@@ -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.");
}
}
@@ -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)
@@ -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()
@@ -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
@@ -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());
@@ -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('^');
@@ -0,0 +1 @@
LocaleData.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;
}
}
@@ -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,14 +213,26 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl
*/
protected synchronized int getTimeToLive() throws IOException
{
Object obj = getOption(VMPlainDatagramSocketImpl.IP_TTL);
if (! (obj instanceof Integer))
throw new IOException("Internal Error");
return ((Integer) obj).intValue();
return impl.getTimeToLive();
}
protected int getLocalPort()
{
if (channel == null)
return -1;
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);
}
}
@@ -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);
if (impl.getState().isValid())
impl.close();
address = null;
port = -1;
}
public void sendUrgentData(int data)
public void sendUrgentData(int data) throws IOException
{
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);
}
/**
* 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
{
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.
}
}
}
}
}
@@ -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)
{
@@ -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;
@@ -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.
}
@@ -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;
}
@@ -148,6 +151,45 @@ public class Handler extends URLStreamHandler
setURL (url, "jar", url.getHost(), url.getPort(), url_string, null);
}
/**
* 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.
*
@@ -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())
if (!(target instanceof InetSocketAddress))
throw new IOException("can only send to inet socket addresses");
InetSocketAddress dst = (InetSocketAddress) target;
if (dst.isUnresolved())
throw new IOException("Target address not resolved");
byte[] buffer;
int offset = 0;
int len = src.remaining();
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);
}
return len;
return channel.send(src, dst);
}
public VMChannel getVMChannel()
{
return channel;
}
}
@@ -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);
}
}
}
@@ -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;
}
}
@@ -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);
}
@@ -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();
}
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 long implPosition() throws IOException
{
return ch.position();
}
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;
}
}*/
}
@@ -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;
@@ -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());
}
}
@@ -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);
}
+16 -13
View File
@@ -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();
}
}
@@ -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.
}
}
+17 -17
View File
@@ -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()
@@ -106,5 +106,6 @@ public abstract class SelectionKeyImpl extends AbstractSelectionKey
return impl;
}
/* @deprecated */
public abstract int getNativeFD();
}
@@ -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 ())
{
@@ -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);
}
@@ -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;
}
}
@@ -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);
}
}
}
@@ -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);
}
SocketChannelImpl (SelectorProvider provider)
public SocketChannelImpl(VMChannel channel) throws IOException
{
this(new SelectorProviderImpl(), channel, false);
}
SocketChannelImpl(SelectorProvider provider) throws IOException
{
this(provider, true);
}
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,22 +132,27 @@ 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();
InetSocketAddress remote = channel.getPeerAddress();
if (remote != null)
{
connectionPending = false;
return true;
}
if (!isConnected() && !connectionPending)
if (!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())
{
selector.select(); // blocking until channel is connected.
connectionPending = false;
return true;
}
int ready = selector.selectNow(); // non-blocking
if (ready == 1)
{
connectionPending = false;
return true;
}
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;
}
}

Some files were not shown because too many files have changed in this diff Show More