KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > bridge > CursorManager


1 /*
2
3    Copyright 2002-2004 The Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16
17  */

18 package org.apache.batik.bridge;
19
20 import java.awt.Cursor JavaDoc;
21 import java.awt.Dimension JavaDoc;
22 import java.awt.Image JavaDoc;
23 import java.awt.Point JavaDoc;
24 import java.awt.Rectangle JavaDoc;
25 import java.awt.Toolkit JavaDoc;
26 import java.awt.geom.AffineTransform JavaDoc;
27 import java.awt.geom.Point2D JavaDoc;
28 import java.awt.image.BufferedImage JavaDoc;
29 import java.awt.image.ColorModel JavaDoc;
30 import java.awt.image.Raster JavaDoc;
31 import java.awt.image.RenderedImage JavaDoc;
32 import java.awt.image.SampleModel JavaDoc;
33 import java.awt.image.WritableRaster JavaDoc;
34 import java.util.Hashtable JavaDoc;
35 import java.util.Map JavaDoc;
36
37 import org.apache.batik.css.engine.SVGCSSEngine;
38 import org.apache.batik.css.engine.value.Value;
39 import org.apache.batik.dom.svg.XMLBaseSupport;
40 import org.apache.batik.dom.util.XLinkSupport;
41 import org.apache.batik.ext.awt.image.PadMode;
42 import org.apache.batik.ext.awt.image.renderable.AffineRable8Bit;
43 import org.apache.batik.ext.awt.image.renderable.Filter;
44 import org.apache.batik.ext.awt.image.renderable.PadRable8Bit;
45 import org.apache.batik.ext.awt.image.spi.ImageTagRegistry;
46 import org.apache.batik.gvt.GraphicsNode;
47 import org.apache.batik.util.ParsedURL;
48 import org.apache.batik.util.SVGConstants;
49 import org.apache.batik.util.SoftReferenceCache;
50 import org.w3c.dom.Element JavaDoc;
51 import org.w3c.dom.Node JavaDoc;
52 import org.w3c.dom.css.CSSPrimitiveValue;
53 import org.w3c.dom.css.CSSValue;
54 import org.w3c.dom.svg.SVGDocument;
55 import org.w3c.dom.svg.SVGPreserveAspectRatio;
56
57
58 /**
59  * The CursorManager class is a helper class which preloads the cursors
60  * corresponding to the SVG built in cursors.
61  *
62  * @author <a HREF="mailto:vincent.hardy@sun.com">Vincent Hardy</a>
63  * @version $Id: CursorManager.java,v 1.17 2005/03/27 08:58:30 cam Exp $
64  */

65 public class CursorManager implements SVGConstants, ErrorConstants {
66     /**
67      * Maps SVG Cursor Values to Java Cursors
68      */

69     protected static Map JavaDoc cursorMap;
70
71     /**
72      * Default cursor when value is not found
73      */

74     public static final Cursor JavaDoc DEFAULT_CURSOR
75         = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
76
77     /**
78      * Cursor used over anchors
79      */

80     public static final Cursor JavaDoc ANCHOR_CURSOR
81         = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
82
83     /**
84      * Cursor used over text
85      */

86     public static final Cursor JavaDoc TEXT_CURSOR
87         = Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR);
88
89     /**
90      * Default preferred cursor size, used for SVG images
91      */

92     public static final int DEFAULT_PREFERRED_WIDTH = 32;
93     public static final int DEFAULT_PREFERRED_HEIGHT = 32;
94
95     /**
96      * Static initialization of the cursorMap
97      */

98     static {
99         cursorMap = new Hashtable JavaDoc();
100         cursorMap.put(SVG_CROSSHAIR_VALUE,
101                       Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
102         cursorMap.put(SVG_DEFAULT_VALUE,
103                       Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
104         cursorMap.put(SVG_POINTER_VALUE,
105                       Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
106         cursorMap.put(SVG_MOVE_VALUE,
107                       Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
108         cursorMap.put(SVG_E_RESIZE_VALUE,
109                       Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
110         cursorMap.put(SVG_NE_RESIZE_VALUE,
111                       Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR));
112         cursorMap.put(SVG_NW_RESIZE_VALUE,
113                       Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR));
114         cursorMap.put(SVG_N_RESIZE_VALUE,
115                       Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR));
116         cursorMap.put(SVG_SE_RESIZE_VALUE,
117                       Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR));
118         cursorMap.put(SVG_SW_RESIZE_VALUE,
119                       Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR));
120         cursorMap.put(SVG_S_RESIZE_VALUE,
121                       Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR));
122         cursorMap.put(SVG_W_RESIZE_VALUE,
123                       Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR));
124         cursorMap.put(SVG_TEXT_VALUE,
125                       Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
126         cursorMap.put(SVG_WAIT_VALUE,
127                       Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
128         cursorMap.put(SVG_HELP_VALUE,
129                       Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
130         
131     }
132
133     /**
134      * BridgeContext associated with this CursorManager
135      */

136     protected BridgeContext ctx;
137
138     /**
139      * Cache used to hold references to cursors
140      */

141     protected CursorCache cursorCache = new CursorCache();
142
143     /**
144      * Creates a new CursorManager object.
145      *
146      * @param ctx the BridgeContext associated to this CursorManager
147      */

148     public CursorManager(BridgeContext ctx) {
149         this.ctx = ctx;
150     }
151
152     /**
153      * Returns a Cursor object for a given cursor value. This initial
154      * implementation does not handle user-defined cursors, so it
155      * always uses the cursor at the end of the list
156      */

157     public static Cursor JavaDoc getPredefinedCursor(String JavaDoc cursorName){
158         return (Cursor JavaDoc)cursorMap.get(cursorName);
159     }
160
161
162
163     /**
164      * Returns the Cursor corresponding to the input element's cursor property
165      *
166      * @param e the element on which the cursor property is set
167      */

168     public Cursor JavaDoc convertCursor(Element JavaDoc e) {
169         Value cursorValue = CSSUtilities.getComputedStyle
170             (e, SVGCSSEngine.CURSOR_INDEX);
171
172         String JavaDoc cursorStr = SVGConstants.SVG_AUTO_VALUE;
173
174         if (cursorValue != null) {
175             if (cursorValue.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE
176                 &&
177                 cursorValue.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) {
178                 // Single Value : should be one of the predefined cursors or
179
// 'inherit'
180
cursorStr = cursorValue.getStringValue();
181                 return convertBuiltInCursor(e, cursorStr);
182             } else if (cursorValue.getCssValueType() ==
183                        CSSValue.CSS_VALUE_LIST) {
184                 int nValues = cursorValue.getLength();
185                 if (nValues == 1) {
186                     cursorValue = cursorValue.item(0);
187                     if (cursorValue.getPrimitiveType() ==
188                         CSSPrimitiveValue.CSS_IDENT) {
189                         cursorStr = cursorValue.getStringValue();
190                         return convertBuiltInCursor(e, cursorStr);
191                     }
192                 } else if (nValues > 1) {
193                     //
194
// Look for the first cursor url we can handle.
195
// That would be a reference to a <cursor> element.
196
//
197
return convertSVGCursor(e, cursorValue);
198                 }
199             }
200         }
201         
202         return convertBuiltInCursor(e, cursorStr);
203     }
204     
205     public Cursor JavaDoc convertBuiltInCursor(Element JavaDoc e, String JavaDoc cursorStr) {
206         Cursor JavaDoc cursor = null;
207
208         // The CSS engine guarantees an non null, non empty string
209
// as the computed value for cursor. Therefore, the following
210
// test is safe.
211
if (cursorStr.charAt(0) == 'a') {
212             //
213
// Handle 'auto' value.
214
//
215
// - <a> The following sets the cursor for <a> element
216
// enclosing text nodes. Setting the proper cursor (i.e.,
217
// depending on the children's 'cursor' property, is
218
// handled in the SVGAElementBridge so as to avoid going
219
// up the tree on mouseover events (looking for an anchor
220
// ancestor.
221
//
222
// - <image> The following does not change the cursor if
223
// the element's cursor property is set to
224
// 'auto'. Otherwise, it takes precedence over any child
225
// (in case of SVG content) cursor setting. This means
226
// that for images referencing SVG content, a cursor
227
// property set to 'auto' on the <image> element will not
228
// override the cursor settings inside the SVG image. Any
229
// other cursor property will take precedence.
230
//
231
// - <use> Same behavior as for <image> except that the
232
// behavior is controlled from the <use> element bridge
233
// (SVGUseElementBridge).
234
//
235
// - <text>, <tref> and <tspan> : a cursor value of auto
236
// will cause the cursor to be set to a text cursor. Note
237
// that text content with an 'auto' cursor and descendant
238
// of an anchor will have its cursor set to the anchor
239
// cursor through the SVGAElementBridge.
240
//
241
String JavaDoc nameSpaceURI = e.getNamespaceURI();
242             if (SVGConstants.SVG_NAMESPACE_URI.equals(nameSpaceURI)) {
243                 String JavaDoc tag = e.getLocalName();
244                 if (SVGConstants.SVG_A_TAG.equals(tag)) {
245                     cursor = CursorManager.ANCHOR_CURSOR;
246                 } else if (SVGConstants.SVG_TEXT_TAG.equals(tag) ||
247                            SVGConstants.SVG_TSPAN_TAG.equals(tag) ||
248                            SVGConstants.SVG_TREF_TAG.equals(tag) ) {
249                     cursor = CursorManager.TEXT_CURSOR;
250                 } else if (SVGConstants.SVG_IMAGE_TAG.equals(tag)) {
251                     // Do not change the cursor
252
return null;
253                 } else {
254                     cursor = CursorManager.DEFAULT_CURSOR;
255                 }
256             } else {
257                 cursor = CursorManager.DEFAULT_CURSOR;
258             }
259         } else {
260             // Specific, logical cursor
261
cursor = CursorManager.getPredefinedCursor(cursorStr);
262         }
263         
264         return cursor;
265     }
266
267        
268     /**
269      * Returns a cursor for the given value list. Note that the
270      * code assumes that the input value has at least two entries.
271      * So the caller should check that before calling the method.
272      * For example, CSSUtilities.convertCursor performs that check.
273      */

274     public Cursor JavaDoc convertSVGCursor(Element JavaDoc e, Value l) {
275         int nValues = l.getLength();
276         Element JavaDoc cursorElement = null;
277         for (int i=0; i<nValues-1; i++) {
278             Value cursorValue = l.item(i);
279             if (cursorValue.getPrimitiveType() == CSSPrimitiveValue.CSS_URI) {
280                 String JavaDoc uri = cursorValue.getStringValue();
281                 
282                 // If the uri does not resolve to a cursor element,
283
// then, this is not a type of cursor uri we can handle:
284
// go to the next or default to logical cursor
285
try {
286                     cursorElement = ctx.getReferencedElement(e, uri);
287                 } catch (BridgeException be) {
288                     // Be only silent if this is a case where the target
289
// could not be found. Do not catch other errors (e.g,
290
// malformed URIs)
291
if (!ERR_URI_BAD_TARGET.equals(be.getCode())) {
292                         throw be;
293                     }
294                 }
295                 
296                 if (cursorElement != null) {
297                     // We go an element, check it is of type cursor
298
String JavaDoc cursorNS = cursorElement.getNamespaceURI();
299                     if (SVGConstants.SVG_NAMESPACE_URI.equals(cursorNS) &&
300                         SVGConstants.SVG_CURSOR_TAG.equals
301                         (cursorElement.getLocalName())) {
302                         Cursor JavaDoc c = convertSVGCursorElement(cursorElement);
303                         if (c != null) {
304                             return c;
305                         }
306                     }
307                 }
308             }
309         }
310         
311         // If we got to that point, it means that no cursorElement
312
// produced a valid cursor, i.e., either a format we support
313
// or a valid referenced image (no broken image).
314
// Fallback on the built in cursor property.
315
Value cursorValue = l.item(nValues-1);
316         String JavaDoc cursorStr = SVGConstants.SVG_AUTO_VALUE;
317         if (cursorValue.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) {
318             cursorStr = cursorValue.getStringValue();
319         }
320           
321         return convertBuiltInCursor(e, cursorStr);
322     }
323
324     /**
325      * Returns a cursor for a given element
326      */

327     public Cursor JavaDoc convertSVGCursorElement(Element JavaDoc cursorElement) {
328         // One of the cursor url resolved to a <cursor> element
329
// Try to handle its image.
330
String JavaDoc uriStr = XLinkSupport.getXLinkHref(cursorElement);
331         if (uriStr.length() == 0) {
332             throw new BridgeException(cursorElement, ERR_ATTRIBUTE_MISSING,
333                                       new Object JavaDoc[] {"xlink:href"});
334         }
335
336         String JavaDoc baseURI = XMLBaseSupport.getCascadedXMLBase(cursorElement);
337         ParsedURL purl;
338         if (baseURI == null) {
339             purl = new ParsedURL(uriStr);
340         } else {
341             purl = new ParsedURL(baseURI, uriStr);
342         }
343
344         //
345
// Convert the cursor's hot spot
346
//
347
UnitProcessor.Context uctx
348             = UnitProcessor.createContext(ctx, cursorElement);
349
350         String JavaDoc s = cursorElement.getAttributeNS(null, SVG_X_ATTRIBUTE);
351         float x = 0;
352         if (s.length() != 0) {
353             x = UnitProcessor.svgHorizontalCoordinateToUserSpace
354                 (s, SVG_X_ATTRIBUTE, uctx);
355         }
356
357         s = cursorElement.getAttributeNS(null, SVG_Y_ATTRIBUTE);
358         float y = 0;
359         if (s.length() != 0) {
360             y = UnitProcessor.svgVerticalCoordinateToUserSpace
361                 (s, SVG_Y_ATTRIBUTE, uctx);
362         }
363
364         CursorDescriptor desc = new CursorDescriptor(purl, x, y);
365
366         //
367
// Check if there is a cursor in the cache for this url
368
//
369
Cursor JavaDoc cachedCursor = cursorCache.getCursor(desc);
370
371         if (cachedCursor != null) {
372             return cachedCursor;
373         }
374         
375         //
376
// Load image into Filter f and transform hotSpot to
377
// cursor space.
378
//
379
Point2D.Float JavaDoc hotSpot = new Point2D.Float JavaDoc(x, y);
380         Filter f = cursorHrefToFilter(cursorElement,
381                                       purl,
382                                       hotSpot);
383         if (f == null) {
384             cursorCache.clearCursor(desc);
385             return null;
386         }
387             
388         // The returned Filter is guaranteed to create a
389
// default rendering of the desired size
390
Rectangle JavaDoc cursorSize = f.getBounds2D().getBounds();
391         RenderedImage JavaDoc ri = f.createScaledRendering(cursorSize.width,
392                                                    cursorSize.height,
393                                                    null);
394         Image JavaDoc img = null;
395
396         if (ri instanceof Image JavaDoc) {
397             img = (Image JavaDoc)ri;
398         } else {
399             img = renderedImageToImage(ri);
400         }
401
402         // Make sure the not spot does not fall out of the cursor area. If it
403
// does, then clamp the coordinates to the image space.
404
hotSpot.x = hotSpot.x < 0 ? 0 : hotSpot.x;
405         hotSpot.y = hotSpot.y < 0 ? 0 : hotSpot.y;
406         hotSpot.x = hotSpot.x > (cursorSize.width-1) ? cursorSize.width - 1 : hotSpot.x;
407         hotSpot.y = hotSpot.y > (cursorSize.height-1) ? cursorSize.height - 1: hotSpot.y;
408
409         //
410
// The cursor image is now into 'img'
411
//
412
Cursor JavaDoc c = Toolkit.getDefaultToolkit()
413             .createCustomCursor(img,
414                                 new Point JavaDoc(Math.round(hotSpot.x),
415                                           Math.round(hotSpot.y)),
416                                 purl.toString());
417
418         cursorCache.putCursor(desc, c);
419         return c;
420     }
421
422     /**
423      * Converts the input ParsedURL into a Filter and transforms the
424      * input hotSpot point (in image space) to cursor space
425      */

426     protected Filter cursorHrefToFilter(Element JavaDoc cursorElement,
427                                         ParsedURL purl,
428                                         Point2D JavaDoc hotSpot) {
429         
430         AffineRable8Bit f = null;
431         String JavaDoc uriStr = purl.toString();
432         Dimension JavaDoc cursorSize = null;
433
434         // Try to load as an SVG Document
435
DocumentLoader loader = ctx.getDocumentLoader();
436         SVGDocument svgDoc = (SVGDocument)cursorElement.getOwnerDocument();
437         URIResolver resolver = new URIResolver(svgDoc, loader);
438         try {
439             Element JavaDoc rootElement = null;
440             Node n = resolver.getNode(uriStr, cursorElement);
441             if (n.getNodeType() == Node.DOCUMENT_NODE) {
442                 SVGDocument doc = (SVGDocument)n;
443                 // FIXX: really should be subCtx here.
444
ctx.initializeDocument(doc);
445                 rootElement = doc.getRootElement();
446             } else {
447                 throw new BridgeException
448                     (cursorElement, ERR_URI_IMAGE_INVALID,
449                      new Object JavaDoc[] {uriStr});
450             }
451             GraphicsNode node = ctx.getGVTBuilder().build(ctx, rootElement);
452
453             //
454
// The cursorSize define the viewport into which the
455
// cursor is displayed. That viewport is platform
456
// dependant and is not defined by the SVG content.
457
//
458
float width = DEFAULT_PREFERRED_WIDTH;
459             float height = DEFAULT_PREFERRED_HEIGHT;
460             UnitProcessor.Context uctx
461                 = UnitProcessor.createContext(ctx, rootElement);
462
463             String JavaDoc s = rootElement.getAttribute(SVG_WIDTH_ATTRIBUTE);
464             if (s.length() != 0) {
465                 width = UnitProcessor.svgHorizontalLengthToUserSpace
466                 (s, SVG_WIDTH_ATTRIBUTE, uctx);
467             }
468             
469             s = rootElement.getAttribute(SVG_HEIGHT_ATTRIBUTE);
470             if (s.length() != 0) {
471                 height = UnitProcessor.svgVerticalLengthToUserSpace
472                     (s, SVG_HEIGHT_ATTRIBUTE, uctx);
473             }
474
475             cursorSize
476                 = Toolkit.getDefaultToolkit().getBestCursorSize
477                 (Math.round(width), Math.round(height));
478             
479             // Handle the viewBox transform
480
AffineTransform JavaDoc at
481                 = ViewBox.getPreserveAspectRatioTransform(rootElement,
482                                                           cursorSize.width,
483                                                           cursorSize.height);
484             Filter filter = node.getGraphicsNodeRable(true);
485             f = new AffineRable8Bit(filter, at);
486         } catch (BridgeException ex) {
487             throw ex;
488         } catch (SecurityException JavaDoc ex) {
489             throw new BridgeException(cursorElement, ERR_URI_UNSECURE,
490                                       new Object JavaDoc[] {uriStr});
491         } catch (Exception JavaDoc ex) {
492             /* Nothing to do */
493         }
494
495
496         // If f is null, it means that we are not dealing with
497
// an SVG image. Try as a raster image.
498
if (f == null) {
499             ImageTagRegistry reg = ImageTagRegistry.getRegistry();
500             Filter filter = reg.readURL(purl);
501             if (filter == null) {
502                 return null;
503             }
504
505             // Check if we got a broken image
506
if (filter.getProperty
507                 (SVGBrokenLinkProvider.SVG_BROKEN_LINK_DOCUMENT_PROPERTY) != null) {
508                 return null;
509             }
510             
511             Rectangle JavaDoc preferredSize = filter.getBounds2D().getBounds();
512             cursorSize = Toolkit.getDefaultToolkit().getBestCursorSize
513                 (preferredSize.width, preferredSize.height);
514             
515             if (preferredSize != null && preferredSize.width >0
516                 && preferredSize.height > 0 ) {
517                 AffineTransform JavaDoc at = new AffineTransform JavaDoc();
518                 if (preferredSize.width > cursorSize.width
519                     ||
520                     preferredSize.height > cursorSize.height) {
521                     at = ViewBox.getPreserveAspectRatioTransform
522                         (new float[] {0, 0, preferredSize.width, preferredSize.height},
523                          SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMINYMIN,
524                          true,
525                          cursorSize.width,
526                          cursorSize.height);
527                 }
528                 f = new AffineRable8Bit(filter, at);
529             } else {
530                 // Invalid Size
531
return null;
532             }
533         }
534
535
536         //
537
// Transform the hot spot from image space to cursor space
538
//
539
AffineTransform JavaDoc at = f.getAffine();
540         at.transform(hotSpot, hotSpot);
541
542         //
543
// In all cases, clip to the cursor boundaries
544
//
545
Rectangle JavaDoc cursorViewport
546             = new Rectangle JavaDoc(0, 0, cursorSize.width, cursorSize.height);
547
548         PadRable8Bit cursorImage
549             = new PadRable8Bit(f, cursorViewport,
550                                PadMode.ZERO_PAD);
551
552         return cursorImage;
553             
554     }
555
556
557     /**
558      * Implementation helper: converts a RenderedImage to an Image
559      */

560     protected Image JavaDoc renderedImageToImage(RenderedImage JavaDoc ri) {
561         int x = ri.getMinX();
562         int y = ri.getMinY();
563         SampleModel JavaDoc sm = ri.getSampleModel();
564         ColorModel JavaDoc cm = ri.getColorModel();
565         WritableRaster JavaDoc wr = Raster.createWritableRaster(sm, new Point JavaDoc(x,y));
566         ri.copyData(wr);
567
568         return new BufferedImage JavaDoc(cm, wr, cm.isAlphaPremultiplied(), null);
569     }
570
571     /**
572      * Simple inner class which holds the information describing
573      * a cursor, i.e., the image it points to and the hot spot point
574      * coordinates.
575      */

576     static class CursorDescriptor {
577         ParsedURL purl;
578         float x;
579         float y;
580         String JavaDoc desc;
581
582         public CursorDescriptor(ParsedURL purl,
583                                 float x, float y) {
584             if (purl == null) {
585                 throw new IllegalArgumentException JavaDoc();
586             }
587
588             this.purl = purl;
589             this.x = x;
590             this.y = y;
591
592             // Desc is used for hascode as well as for toString()
593
this.desc = this.getClass().getName() +
594                 "\n\t:[" + this.purl + "]\n\t:[" + x + "]:[" + y + "]";
595         }
596
597         public boolean equals(Object JavaDoc obj) {
598             if (obj == null
599                 ||
600                 !(obj instanceof CursorDescriptor)) {
601                 return false;
602             }
603
604             CursorDescriptor desc = (CursorDescriptor)obj;
605             boolean isEqual =
606                 this.purl.equals(desc.purl)
607                  &&
608                  this.x == desc.x
609                  &&
610                  this.y == desc.y;
611                  
612             return isEqual;
613         }
614
615         public String JavaDoc toString() {
616             return this.desc;
617         }
618
619         public int hashCode() {
620             return desc.hashCode();
621         }
622     }
623
624     /**
625      * Simple extension of the SoftReferenceCache that
626      * offers typed interface (Kind of needed as SoftReferenceCache
627      * mostly has protected methods).
628      */

629     static class CursorCache extends SoftReferenceCache {
630         public CursorCache() {
631         }
632
633         public Cursor JavaDoc getCursor(CursorDescriptor desc) {
634             return (Cursor JavaDoc)requestImpl(desc);
635         }
636
637         public void putCursor(CursorDescriptor desc,
638                               Cursor JavaDoc cursor) {
639             putImpl(desc, cursor);
640         }
641
642         public void clearCursor(CursorDescriptor desc) {
643             clearImpl(desc);
644         }
645     }
646
647
648
649 }
650
Popular Tags