1 /*
   2  * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /*
  27  * @author Charlton Innovations, Inc.
  28  */
  29 
  30 package sun.java2d.loops;
  31 
  32 import java.awt.image.BufferedImage;
  33 import java.awt.AlphaComposite;
  34 import java.awt.Rectangle;
  35 import sun.awt.image.BufImgSurfaceData;
  36 import sun.awt.util.ThreadGroupUtils;
  37 import sun.java2d.SurfaceData;
  38 import sun.java2d.pipe.Region;
  39 import java.lang.reflect.Field;
  40 import java.util.StringTokenizer;
  41 import java.util.Iterator;
  42 import java.util.HashMap;
  43 import java.util.HashSet;
  44 import java.util.Map;
  45 import java.io.PrintStream;
  46 import java.io.OutputStream;
  47 import java.io.FileOutputStream;
  48 import java.io.FileNotFoundException;
  49 import java.security.AccessController;
  50 import java.security.PrivilegedAction;
  51 
  52 import sun.security.action.GetPropertyAction;
  53 
  54 /**
  55  * defines interface for primitives which can be placed into
  56  * the graphic component manager framework
  57  */
  58 public abstract class GraphicsPrimitive {
  59 
  60     protected static interface GeneralBinaryOp {
  61         /**
  62          * This method allows the setupGeneralBinaryOp method to set
  63          * the converters into the General version of the Primitive.
  64          */
  65         public void setPrimitives(Blit srcconverter,
  66                                   Blit dstconverter,
  67                                   GraphicsPrimitive genericop,
  68                                   Blit resconverter);
  69 
  70         /**
  71          * These 4 methods are implemented automatically for any
  72          * GraphicsPrimitive.  They are used by setupGeneralBinaryOp
  73          * to retrieve the information needed to find the right
  74          * converter primitives.
  75          */
  76         public SurfaceType getSourceType();
  77         public CompositeType getCompositeType();
  78         public SurfaceType getDestType();
  79         public String getSignature();
  80         public int getPrimTypeID();
  81     }
  82 
  83     protected static interface GeneralUnaryOp {
  84         /**
  85          * This method allows the setupGeneralUnaryOp method to set
  86          * the converters into the General version of the Primitive.
  87          */
  88         public void setPrimitives(Blit dstconverter,
  89                                   GraphicsPrimitive genericop,
  90                                   Blit resconverter);
  91 
  92         /**
  93          * These 3 methods are implemented automatically for any
  94          * GraphicsPrimitive.  They are used by setupGeneralUnaryOp
  95          * to retrieve the information needed to find the right
  96          * converter primitives.
  97          */
  98         public CompositeType getCompositeType();
  99         public SurfaceType getDestType();
 100         public String getSignature();
 101         public int getPrimTypeID();
 102     }
 103 
 104     /**
 105     *  INSTANCE DATA MEMBERS DESCRIBING CHARACTERISTICS OF THIS PRIMITIVE
 106     **/
 107 
 108     // Making these be instance data members (instead of virtual methods
 109     // overridden by subclasses) is actually cheaper, since each class
 110     // is a singleton.  As instance data members with final accessors,
 111     // accesses can be inlined.
 112     private String methodSignature;
 113     private int uniqueID;
 114     private static int unusedPrimID = 1;
 115 
 116     private SurfaceType sourceType;
 117     private CompositeType compositeType;
 118     private SurfaceType destType;
 119 
 120     private long pNativePrim;   // Native blit loop info
 121 
 122     public static final synchronized int makePrimTypeID() {
 123         if (unusedPrimID > 255) {
 124             throw new InternalError("primitive id overflow");
 125         }
 126         return unusedPrimID++;
 127     }
 128 
 129     public static final synchronized int makeUniqueID(int primTypeID,
 130                                                       SurfaceType src,
 131                                                       CompositeType cmp,
 132                                                       SurfaceType dst)
 133     {
 134         return (primTypeID << 24) |
 135             (dst.getUniqueID() << 16) |
 136             (cmp.getUniqueID() << 8)  |
 137             (src.getUniqueID());
 138     }
 139 
 140     /**
 141      * Create a new GraphicsPrimitive with all of the required
 142      * descriptive information.
 143      */
 144     protected GraphicsPrimitive(String methodSignature,
 145                                 int primTypeID,
 146                                 SurfaceType sourceType,
 147                                 CompositeType compositeType,
 148                                 SurfaceType destType)
 149     {
 150         this.methodSignature = methodSignature;
 151         this.sourceType = sourceType;
 152         this.compositeType = compositeType;
 153         this.destType = destType;
 154 
 155         if(sourceType == null || compositeType == null || destType == null) {
 156             this.uniqueID = primTypeID << 24;
 157         } else {
 158             this.uniqueID = GraphicsPrimitive.makeUniqueID(primTypeID,
 159                                                            sourceType,
 160                                                            compositeType,
 161                                                            destType);
 162         }
 163     }
 164 
 165     /**
 166      * Create a new GraphicsPrimitive for native invocation
 167      * with all of the required descriptive information.
 168      */
 169     protected GraphicsPrimitive(long pNativePrim,
 170                                 String methodSignature,
 171                                 int primTypeID,
 172                                 SurfaceType sourceType,
 173                                 CompositeType compositeType,
 174                                 SurfaceType destType)
 175     {
 176         this.pNativePrim = pNativePrim;
 177         this.methodSignature = methodSignature;
 178         this.sourceType = sourceType;
 179         this.compositeType = compositeType;
 180         this.destType = destType;
 181 
 182         if(sourceType == null || compositeType == null || destType == null) {
 183             this.uniqueID = primTypeID << 24;
 184         } else {
 185             this.uniqueID = GraphicsPrimitive.makeUniqueID(primTypeID,
 186                                                            sourceType,
 187                                                            compositeType,
 188                                                            destType);
 189         }
 190     }
 191 
 192     /**
 193     *   METHODS TO DESCRIBE THE SURFACES PRIMITIVES
 194     *   CAN OPERATE ON AND THE FUNCTIONALITY THEY IMPLEMENT
 195     **/
 196 
 197     /**
 198      * Gets instance ID of this graphics primitive.
 199      *
 200      * Instance ID is comprised of four distinct ids (ORed together)
 201      * that uniquely identify each instance of a GraphicsPrimitive
 202      * object. The four ids making up instance ID are:
 203      * 1. primitive id - identifier shared by all primitives of the
 204      * same type (eg. all Blits have the same primitive id)
 205      * 2. sourcetype id - identifies source surface type
 206      * 3. desttype id - identifies destination surface type
 207      * 4. compositetype id - identifies composite used
 208      *
 209      * @return instance ID
 210      */
 211     public final int getUniqueID() {
 212         return uniqueID;
 213     }
 214 
 215     /**
 216      */
 217     public final String getSignature() {
 218         return methodSignature;
 219     }
 220 
 221     /**
 222      * Gets unique id for this GraphicsPrimitive type.
 223      *
 224      * This id is used to identify the TYPE of primitive (Blit vs. BlitBg)
 225      * as opposed to INSTANCE of primitive.
 226      *
 227      * @return primitive ID
 228      */
 229     public final int getPrimTypeID() {
 230         return uniqueID >>> 24;
 231     }
 232 
 233     /**
 234      */
 235     public final long getNativePrim() {
 236         return pNativePrim;
 237     }
 238 
 239     /**
 240      */
 241     public final SurfaceType getSourceType() {
 242         return sourceType;
 243     }
 244 
 245     /**
 246      */
 247     public final CompositeType getCompositeType() {
 248         return compositeType;
 249     }
 250 
 251     /**
 252      */
 253     public final SurfaceType getDestType() {
 254         return destType;
 255     }
 256 
 257     /**
 258      * Return true if this primitive can be used for the given signature
 259      * surfaces, and composite.
 260      *
 261      * @param signature The signature of the given operation.  Must be
 262      *          == (not just .equals) the signature string given by the
 263      *          abstract class that declares the operation.
 264      * @param srctype The surface type for the source of the operation
 265      * @param comptype The composite type for the operation
 266      * @param dsttype The surface type for the destination of the operation
 267      */
 268     public final boolean satisfies(String signature,
 269                                    SurfaceType srctype,
 270                                    CompositeType comptype,
 271                                    SurfaceType dsttype)
 272     {
 273         if (signature != methodSignature) {
 274             return false;
 275         }
 276         while (true) {
 277             if (srctype == null) {
 278                 return false;
 279             }
 280             if (srctype.equals(sourceType)) {
 281                 break;
 282             }
 283             srctype = srctype.getSuperType();
 284         }
 285         while (true) {
 286             if (comptype == null) {
 287                 return false;
 288             }
 289             if (comptype.equals(compositeType)) {
 290                 break;
 291             }
 292             comptype = comptype.getSuperType();
 293         }
 294         while (true) {
 295             if (dsttype == null) {
 296                 return false;
 297             }
 298             if (dsttype.equals(destType)) {
 299                 break;
 300             }
 301             dsttype = dsttype.getSuperType();
 302         }
 303         return true;
 304     }
 305 
 306     //
 307     // A version of satisfies used for regression testing
 308     //
 309     final boolean satisfiesSameAs(GraphicsPrimitive other) {
 310         return (methodSignature == other.methodSignature &&
 311                 sourceType.equals(other.sourceType) &&
 312                 compositeType.equals(other.compositeType) &&
 313                 destType.equals(other.destType));
 314     }
 315 
 316     public abstract GraphicsPrimitive makePrimitive(SurfaceType srctype,
 317                                                     CompositeType comptype,
 318                                                     SurfaceType dsttype);
 319 
 320     public abstract GraphicsPrimitive traceWrap();
 321 
 322     static HashMap<Object, int[]> traceMap;
 323     static HashSet<String> traceNotImplSet;
 324 
 325     public static int traceflags;
 326     public static String tracefile;
 327     public static String pname;
 328     public static PrintStream traceout;
 329     public static long treshold = 0;
 330     public static boolean verbose = false;
 331 
 332     public static final int TRACELOG = 1;
 333     public static final int TRACETIMESTAMP = 2;
 334     public static final int TRACECOUNTS = 4;
 335     public static final int TRACEPTIME = 8;
 336     public static final int TRACEPNAME = 16;
 337     public static final int TRACENOTIMPL = 32;
 338 
 339     static void showTraceUsage() {
 340         System.err.println("usage: -Dsun.java2d.trace="+
 341                 "[log[,timestamp]],[count],[ptime],[name:<substr pattern>],"+
 342                 "[out:<filename>],[td=<treshold>],[help],[verbose]");
 343     }
 344 
 345     static {
 346         GetPropertyAction gpa = new GetPropertyAction("sun.java2d.trace");
 347         String trace = AccessController.doPrivileged(gpa);
 348         if (trace != null) {
 349             int traceflags = 0;
 350             StringTokenizer st = new StringTokenizer(trace, ",");
 351             while (st.hasMoreTokens()) {
 352                 String tok = st.nextToken();
 353                 if (tok.equalsIgnoreCase("count")) {
 354                     traceflags |= GraphicsPrimitive.TRACECOUNTS;
 355                 } else if (tok.equalsIgnoreCase("log")) {
 356                     traceflags |= GraphicsPrimitive.TRACELOG;
 357                 } else if (tok.equalsIgnoreCase("timestamp")) {
 358                     traceflags |= GraphicsPrimitive.TRACETIMESTAMP;
 359                 } else if (tok.equalsIgnoreCase("ptime")) {
 360                     traceflags |=GraphicsPrimitive.TRACEPTIME;
 361                 } else if (tok.equalsIgnoreCase("notimpl")) {
 362                     traceflags |=GraphicsPrimitive.TRACENOTIMPL;
 363                 } else if (tok.regionMatches(true, 0, "name:", 0, 5)) {
 364                     traceflags |=GraphicsPrimitive.TRACEPNAME;
 365                     pname = tok.substring(6);
 366                 } else if (tok.equalsIgnoreCase("verbose")) {
 367                     verbose = true;
 368                 } else if (tok.regionMatches(true, 0, "out:", 0, 4)) {
 369                     tracefile = tok.substring(4);
 370                 } else if (tok.regionMatches(true, 0, "td=", 0, 3)) {
 371                     try {
 372                         treshold = Long.parseLong(tok.substring(3));
 373                     } catch (NumberFormatException e) {
 374                         showTraceUsage();
 375                     }
 376                 } else {
 377                     if (!tok.equalsIgnoreCase("help")) {
 378                         System.err.println("unrecognized token: "+tok);
 379                     }
 380                     showTraceUsage();
 381                 }
 382             }
 383 
 384             GraphicsPrimitiveMgr.setTraceFlags(traceflags);
 385 
 386             if (verbose) {
 387                 System.err.print("GraphicsPrimitive logging ");
 388                 if ((traceflags & GraphicsPrimitive.TRACELOG) != 0) {
 389                     System.err.println("enabled");
 390                     System.err.print("GraphicsPrimitive timetamps ");
 391                     if ((traceflags & GraphicsPrimitive.TRACETIMESTAMP) != 0) {
 392                         System.err.println("enabled");
 393                     } else {
 394                         System.err.println("disabled");
 395                     }
 396                 } else {
 397                     System.err.println("[and timestamps] disabled");
 398                 }
 399                 System.err.print("GraphicsPrimitive invocation counts ");
 400                 if ((traceflags & GraphicsPrimitive.TRACECOUNTS) != 0) {
 401                     System.err.println("enabled");
 402                 } else {
 403                     System.err.println("disabled");
 404                 }
 405                 System.err.print("GraphicsPrimitive trace output to ");
 406                 if (tracefile == null) {
 407                     System.err.println("System.err");
 408                 } else {
 409                     System.err.println("file '"+tracefile+"'");
 410                 }
 411             }
 412             GraphicsPrimitive.traceflags = traceflags;
 413         }
 414     }
 415 
 416     public static boolean tracingEnabled() {
 417         return (traceflags != 0);
 418     }
 419 
 420     private static PrintStream getTraceOutputFile() {
 421         if (traceout == null) {
 422             if (tracefile != null) {
 423                 FileOutputStream o = AccessController.doPrivileged(
 424                     new PrivilegedAction<FileOutputStream>() {
 425                         public FileOutputStream run() {
 426                             try {
 427                                 return new FileOutputStream(tracefile);
 428                             } catch (FileNotFoundException e) {
 429                                 return null;
 430                             }
 431                         }
 432                     });
 433                 if (o != null) {
 434                     traceout = new PrintStream(o);
 435                 } else {
 436                     traceout = System.err;
 437                 }
 438             } else {
 439                 traceout = System.err;
 440             }
 441         }
 442         return traceout;
 443     }
 444 
 445     public static class TraceReporter implements Runnable {
 446         private static boolean hookEnabled = false;
 447 
 448         public static synchronized void setShutdownHook() {
 449             if (hookEnabled) return;
 450             hookEnabled = true;
 451 
 452             AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
 453                 TraceReporter t = new TraceReporter();
 454                 Thread thread = new Thread(
 455                         ThreadGroupUtils.getRootThreadGroup(), t,
 456                         "TraceReporter", 0, false);
 457                 thread.setContextClassLoader(null);
 458                 Runtime.getRuntime().addShutdownHook(thread);
 459                 return null;
 460             });
 461         }
 462 
 463         public void run() {
 464             PrintStream ps = getTraceOutputFile();
 465             if (traceMap != null) {
 466                 Iterator<Map.Entry<Object, int[]>> iterator =
 467                         traceMap.entrySet().iterator();
 468                 long total = 0;
 469                 int numprims = 0;
 470                 while (iterator.hasNext()) {
 471                     Map.Entry<Object, int[]> me = iterator.next();
 472                     Object prim = me.getKey();
 473                     int[] count = me.getValue();
 474                     if (count[0] == 1) {
 475                         ps.print("1 call to ");
 476                     } else {
 477                         ps.print(count[0] + " calls to ");
 478                     }
 479                     ps.println(prim);
 480                     numprims++;
 481                     total += count[0];
 482                 }
 483                 if (numprims == 0) {
 484                     ps.println("No graphics primitives executed");
 485                 } else if (numprims > 1) {
 486                     ps.println(total + " total calls to " +
 487                             numprims + " different primitives");
 488                 }
 489             }
 490 
 491             if (traceNotImplSet != null) {
 492                 ps.println("Not implemented graphics primitives:");
 493 
 494                 for (String name : traceNotImplSet) {
 495                     ps.println(name);
 496                 }
 497             }
 498         }
 499     }
 500 
 501     public synchronized static void tracePrimitive(Object prim) {
 502         if ((traceflags & TRACEPNAME) != 0) {
 503             if (!prim.toString().contains(pname)) return;
 504         }
 505 
 506         if ((traceflags & TRACECOUNTS) != 0) {
 507             if (traceMap == null) {
 508                 traceMap = new HashMap<>();
 509                 TraceReporter.setShutdownHook();
 510             }
 511             int[] o = traceMap.get(prim);
 512             if (o == null) {
 513                 o = new int[1];
 514                 traceMap.put(prim, o);
 515             }
 516             o[0]++;
 517         }
 518         if ((traceflags & TRACELOG) != 0) {
 519             PrintStream ps = getTraceOutputFile();
 520             if ((traceflags & TRACETIMESTAMP) != 0) {
 521                 ps.print(System.currentTimeMillis()+": ");
 522             }
 523             ps.println(prim);
 524         }
 525     }
 526 
 527     public synchronized static void traceNotImplPrimitive(Object prim) {
 528         if ((traceflags & TRACEPNAME) != 0) {
 529             if (!prim.toString().contains(pname)) return;
 530         }
 531 
 532         if ((traceflags & TRACECOUNTS) != 0) {
 533             if (traceNotImplSet == null) {
 534                 traceNotImplSet = new HashSet<String>();
 535                 TraceReporter.setShutdownHook();
 536             }
 537             traceNotImplSet.add(prim.toString());
 538         }
 539         if ((traceflags & TRACELOG) != 0) {
 540             PrintStream ps = getTraceOutputFile();
 541             if ((traceflags & TRACETIMESTAMP) != 0) {
 542                 ps.print(System.currentTimeMillis()+":[NOT IMPL] ");
 543             }
 544             ps.println(prim);
 545         }
 546     }
 547 
 548     public synchronized static void tracePrimitiveTime(Object prim, long time) {
 549         if ((traceflags & TRACEPNAME) != 0) {
 550             if (!prim.toString().contains(pname)) return;
 551         }
 552         if (time > treshold && (traceflags & TRACEPTIME) != 0  && (traceflags & TRACELOG) != 0) {
 553             PrintStream ps = getTraceOutputFile();
 554             ps.println(prim + " time: " + time);
 555             if (verbose) {
 556                 final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
 557                 if (stackTrace.length > 3) {
 558                     for (int i = 3; i < stackTrace.length; i++) {
 559                         ps.println("  " + stackTrace[i].toString());
 560                     }
 561                 }
 562                 ps.println();
 563             }
 564         }
 565     }
 566 
 567     protected void setupGeneralBinaryOp(GeneralBinaryOp gbo) {
 568         int primID = gbo.getPrimTypeID();
 569         String methodSignature = gbo.getSignature();
 570         SurfaceType srctype = gbo.getSourceType();
 571         CompositeType comptype = gbo.getCompositeType();
 572         SurfaceType dsttype = gbo.getDestType();
 573         Blit convertsrc, convertdst, convertres;
 574         GraphicsPrimitive performop;
 575 
 576         convertsrc = createConverter(srctype, SurfaceType.IntArgb);
 577         performop = GraphicsPrimitiveMgr.locatePrim(primID,
 578                                                     SurfaceType.IntArgb,
 579                                                     comptype, dsttype);
 580         if (performop != null) {
 581             convertdst = null;
 582             convertres = null;
 583         } else {
 584             performop = getGeneralOp(primID, comptype);
 585             if (performop == null) {
 586                 throw new InternalError("Cannot construct general op for "+
 587                                         methodSignature+" "+comptype);
 588             }
 589             convertdst = createConverter(dsttype, SurfaceType.IntArgb);
 590             convertres = createConverter(SurfaceType.IntArgb, dsttype);
 591         }
 592 
 593         gbo.setPrimitives(convertsrc, convertdst, performop, convertres);
 594     }
 595 
 596     protected void setupGeneralUnaryOp(GeneralUnaryOp guo) {
 597         int primID = guo.getPrimTypeID();
 598         String methodSignature = guo.getSignature();
 599         CompositeType comptype = guo.getCompositeType();
 600         SurfaceType dsttype = guo.getDestType();
 601 
 602         Blit convertdst = createConverter(dsttype, SurfaceType.IntArgb);
 603         GraphicsPrimitive performop = getGeneralOp(primID, comptype);
 604         Blit convertres = createConverter(SurfaceType.IntArgb, dsttype);
 605         if (convertdst == null || performop == null || convertres == null) {
 606             throw new InternalError("Cannot construct binary op for "+
 607                                     comptype+" "+dsttype);
 608         }
 609 
 610         guo.setPrimitives(convertdst, performop, convertres);
 611     }
 612 
 613     protected static Blit createConverter(SurfaceType srctype,
 614                                           SurfaceType dsttype)
 615     {
 616         if (srctype.equals(dsttype)) {
 617             return null;
 618         }
 619         Blit cv = Blit.getFromCache(srctype, CompositeType.SrcNoEa, dsttype);
 620         if (cv == null) {
 621             throw new InternalError("Cannot construct converter for "+
 622                                     srctype+"=>"+dsttype);
 623         }
 624         return cv;
 625     }
 626 
 627     protected static SurfaceData convertFrom(Blit ob, SurfaceData srcData,
 628                                              int srcX, int srcY, int w, int h,
 629                                              SurfaceData dstData)
 630     {
 631         return convertFrom(ob, srcData,
 632                            srcX, srcY, w, h, dstData,
 633                            BufferedImage.TYPE_INT_ARGB);
 634     }
 635 
 636     protected static SurfaceData convertFrom(Blit ob, SurfaceData srcData,
 637                                              int srcX, int srcY, int w, int h,
 638                                              SurfaceData dstData, int type)
 639     {
 640         if (dstData != null) {
 641             Rectangle r = dstData.getBounds();
 642             if (w > r.width || h > r.height) {
 643                 dstData = null;
 644             }
 645         }
 646         if (dstData == null) {
 647             BufferedImage dstBI = new BufferedImage(w, h, type);
 648             dstData = BufImgSurfaceData.createData(dstBI);
 649         }
 650         ob.Blit(srcData, dstData, AlphaComposite.Src, null,
 651                 srcX, srcY, 0, 0, w, h);
 652         return dstData;
 653     }
 654 
 655     protected static void convertTo(Blit ob,
 656                                     SurfaceData srcImg, SurfaceData dstImg,
 657                                     Region clip,
 658                                     int dstX, int dstY, int w, int h)
 659     {
 660         if (ob != null) {
 661             ob.Blit(srcImg, dstImg, AlphaComposite.Src, clip,
 662                     0, 0, dstX, dstY, w, h);
 663         }
 664     }
 665 
 666     protected static GraphicsPrimitive getGeneralOp(int primID,
 667                                                     CompositeType comptype)
 668     {
 669         return GraphicsPrimitiveMgr.locatePrim(primID,
 670                                                SurfaceType.IntArgb,
 671                                                comptype,
 672                                                SurfaceType.IntArgb);
 673     }
 674 
 675     public static String simplename(Field[] fields, Object o) {
 676         for (int i = 0; i < fields.length; i++) {
 677             Field f = fields[i];
 678             try {
 679                 if (o == f.get(null)) {
 680                     return f.getName();
 681                 }
 682             } catch (Exception e) {
 683             }
 684         }
 685         return "\""+o.toString()+"\"";
 686     }
 687 
 688     public static String simplename(SurfaceType st) {
 689         return simplename(SurfaceType.class.getDeclaredFields(), st);
 690     }
 691 
 692     public static String simplename(CompositeType ct) {
 693         return simplename(CompositeType.class.getDeclaredFields(), ct);
 694     }
 695 
 696     private String cachedname;
 697 
 698     public String toString() {
 699         if (cachedname == null) {
 700             String sig = methodSignature;
 701             int index = sig.indexOf('(');
 702             if (index >= 0) {
 703                 sig = sig.substring(0, index);
 704             }
 705             cachedname = (getClass().getName()+"::"+
 706                           sig+"("+
 707                           simplename(sourceType)+", "+
 708                           simplename(compositeType)+", "+
 709                           simplename(destType)+")");
 710         }
 711         return cachedname;
 712     }
 713 }