Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: c04c5b1e2a8da2cabe668a56a46e8223147a768b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
/*******************************************************************************
 * Copyright (c) 2008, 2015 SAP AG, IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    SAP AG - initial API and implementation
 *    Benjamin Maskalla - patch for 318618, use createFromURL
 *    IBM Corporation - icon labels
 *******************************************************************************/
package org.eclipse.mat.ui;

import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.logging.Logger;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.dynamichelpers.ExtensionTracker;
import org.eclipse.core.runtime.dynamichelpers.IExtensionTracker;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.mat.query.registry.QueryDescriptor;
import org.eclipse.mat.ui.internal.ErrorLogHandler;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;

public class MemoryAnalyserPlugin extends AbstractUIPlugin
{
    public static final String PLUGIN_ID = "org.eclipse.mat.ui"; //$NON-NLS-1$
    public static final String EDITOR_ID = "org.eclipse.mat.ui.editors.HeapEditor"; //$NON-NLS-1$

    private static final String PREFIX = "$nl$/icons/"; //$NON-NLS-1$
    private static final String HEAPPREFIX = PREFIX + "heapobjects/"; //$NON-NLS-1$

    public interface ISharedImages
    {
        String HEAP = HEAPPREFIX + "heapdump16.gif"; //$NON-NLS-1$
        String HEAP_INFO = PREFIX + "heapdump_details.gif"; //$NON-NLS-1$
        String OPEN_SNAPSHOT = PREFIX + "open_snapshot.gif";//$NON-NLS-1$
        String CONSOLE = PREFIX + "console.gif"; //$NON-NLS-1$
        String CONSOLE_PLUS = PREFIX + "console_plus.gif"; //$NON-NLS-1$
        String CONSOLE_REMOVE = PREFIX + "remove_console.gif"; //$NON-NLS-1$
        String COPY = PREFIX + "copy.gif"; //$NON-NLS-1$
        String PLUS = PREFIX + "plus.gif"; //$NON-NLS-1$
        String EXPERT_SYSTEM = PREFIX + "expert.gif"; //$NON-NLS-1$

        String REFRESH = PREFIX + "refresh.gif"; //$NON-NLS-1$       
        String THREAD = PREFIX + "thread.gif"; //$NON-NLS-1$       

        String RETAINED_SET = PREFIX + "retainedSet.gif"; //$NON-NLS-1$
        String PACKAGE = PREFIX + "package.gif"; //$NON-NLS-1$

        String SYNCED = PREFIX + "synced.gif"; //$NON-NLS-1$
        String SYNCED_DISABLED = PREFIX + "synced_disabled.gif"; //$NON-NLS-1$

        String ID = PREFIX + "id.gif"; //$NON-NLS-1$
        String SIZE = PREFIX + "size.gif"; //$NON-NLS-1$

        String CLASS = HEAPPREFIX + "class.gif"; //$NON-NLS-1$
        String CLASS_MIXED = HEAPPREFIX + "class_mixed.gif"; //$NON-NLS-1$
        String CLASS_OLD = HEAPPREFIX + "class_old.gif"; //$NON-NLS-1$
        String SUPERCLASS = HEAPPREFIX + "superclass.gif"; //$NON-NLS-1$
        String NOTEPAD = PREFIX + "notepad.gif"; //$NON-NLS-1$
        String ARGUMENTS_WIZARD = PREFIX + "fill_arguments_wiz.gif"; //$NON-NLS-1$

        String QUERY = PREFIX + "query_browser.gif"; //$NON-NLS-1$
        String QUERY_DISABLED = PREFIX + "query_disabled.gif"; //$NON-NLS-1$
        String OQL = PREFIX + "oql.gif"; //$NON-NLS-1$

        String IMPORT_REPORT = PREFIX + "import_report.gif"; //$NON-NLS-1$
        String EXPORT_MENU = PREFIX + "export.gif"; //$NON-NLS-1$
        String EXPORT_HTML = PREFIX + "export_html.gif"; //$NON-NLS-1$
        String EXPORT_CSV = PREFIX + "export_csv.gif"; //$NON-NLS-1$
        String EXPORT_TXT = PREFIX + "export_txt.gif"; //$NON-NLS-1$

        String REFRESHING = PREFIX + "refreshing.gif"; //$NON-NLS-1$

        String CALCULATOR = PREFIX + "calculator.gif";//$NON-NLS-1$

        String FILTER = PREFIX + "filter.gif"; //$NON-NLS-1$

        String GROUPING = PREFIX + "grouping.gif"; //$NON-NLS-1$

        String COMPARE = PREFIX + "compare.gif"; //$NON-NLS-1$
        String PERCENTAGE = PREFIX + "percentage.gif"; //$NON-NLS-1$

        String INFO = PREFIX + "info.gif"; //$NON-NLS-1$
        String HELP = PREFIX + "help.png"; //$NON-NLS-1$

        String FIND = PREFIX + "find.gif"; //$NON-NLS-1$  
        String EXECUTE_QUERY = PREFIX + "execute_query.gif"; //$NON-NLS-1$
        String SHOW_AS_HISTOGRAM = PREFIX + "as_histogram.gif"; //$NON-NLS-1$  
        String EXPLORE = PREFIX + "explore.gif"; //$NON-NLS-1$  

        String SHOW_PANE = PREFIX + "show_pane.gif"; //$NON-NLS-1$  
        String CLOSE_PANE = PREFIX + "close_pane.gif"; //$NON-NLS-1$  
        String CLOSE_BRANCH = PREFIX + "close_branch.gif"; //$NON-NLS-1$  

        String PINNED = PREFIX + "pinned.gif"; //$NON-NLS-1$

        String MOVE_UP = PREFIX + "move_up.gif"; //$NON-NLS-1$
        String MOVE_DOWN = PREFIX + "move_down.gif"; //$NON-NLS-1$
        String REMOVE = PREFIX + "remove.gif"; //$NON-NLS-1$
        String REMOVE_ALL = PREFIX + "removeall.gif"; //$NON-NLS-1$
        String SELECT_COLUMN = PREFIX + "select_table.gif"; //$NON-NLS-1$
    }

    private static MemoryAnalyserPlugin plugin;

    private Map<ImageDescriptor, Image> imageCache = new HashMap<ImageDescriptor, Image>(20);
    private Map<URI, ImageDescriptor> imagePathCache = new HashMap<URI, ImageDescriptor>(20);
    private IExtensionTracker tracker;
    private Logger logger;
    private ErrorLogHandler errorLogHandler;
    private boolean useParentHandlers;

    // Mappings to permit textual descriptions of Images to be recovered from
    // Images.
    private Map<Image, String> imageTextMap = new HashMap<Image, String>(20);
    private Map<ImageDescriptor, String> descriptorTextMap = new HashMap<ImageDescriptor, String>(20);

    public MemoryAnalyserPlugin()
    {}

    @Override
    public void start(BundleContext context) throws Exception
    {
        super.start(context);
        plugin = this;

        tracker = new ExtensionTracker(Platform.getExtensionRegistry());

        // redirect logging from the analysis core into the Eclipse logging
        // facility
        logger = Logger.getLogger("org.eclipse.mat");//$NON-NLS-1$
        useParentHandlers = logger.getUseParentHandlers();
        logger.setUseParentHandlers(false);
        errorLogHandler = new ErrorLogHandler();
        logger.addHandler(errorLogHandler);
    }

    public void stop(BundleContext context) throws Exception
    {
        plugin = null;

        tracker.close();

        for (Image image : imageCache.values())
            image.dispose();
        imageCache.clear();
        // Clear mappings from Image/Descriptor to descriptive text.
        imageTextMap.clear();
        descriptorTextMap.clear();

        logger.removeHandler(errorLogHandler);
        logger.setUseParentHandlers(useParentHandlers);
        logger = null;
        errorLogHandler = null;

        super.stop(context);
    }

    public static MemoryAnalyserPlugin getDefault()
    {
        return plugin;
    }

    // //////////////////////////////////////////////////////////////
    // image handling
    // //////////////////////////////////////////////////////////////

    public static ImageDescriptor getImageDescriptor(String path)
    {
        // Use singleton instance so that ImageDescriptor can be mapped to text.
        return MemoryAnalyserPlugin.getDefault().getPluginImageDescriptor(path);
    }

    public static Image getImage(String name)
    {
        return MemoryAnalyserPlugin.getDefault().getImage(getImageDescriptor(name));
    }

    private ImageDescriptor getPluginImageDescriptor(String path)
    {
        ImageDescriptor descriptor = AbstractUIPlugin.imageDescriptorFromPlugin(PLUGIN_ID, path);
        if (descriptor != null)
        { // Add map entry for new descriptor to appropriate text.
          // This should not result in a memory leak, assuming that two
          // equivalent ImageDescriptors match under equals().
          // This is already assumed in the usage of imageCache.
            descriptorTextMap.put(descriptor, getIconString(path));
        }
        return descriptor;
    }

    public Image getImage(ImageDescriptor descriptor)
    {
        Image image = imageCache.get(descriptor);
        if (image == null && descriptor != null)
        {
            image = descriptor.createImage();
            imageCache.put(descriptor, image);
            // Map new Image to descriptive text.
            // Should not cause memory leak as this must be a new descriptor.
            imageTextMap.put(image, descriptorTextMap.get(descriptor));
        }
        return image;
    }

    public ImageDescriptor getImageDescriptor(URL path)
    {
        // Use URI for maps to avoid blocking equals operation
        URI pathKey;
        try
        {
            pathKey = path.toURI();
        }
        catch (URISyntaxException e)
        {
            // Will cause a missing image to be used instead
            pathKey = null;
        }
        ImageDescriptor descriptor = imagePathCache.get(pathKey);
        if (descriptor == null)
        {
            descriptor = ImageDescriptor.createFromURL(path);
            imagePathCache.put(pathKey, descriptor);
            // Map new descriptor to descriptive text for the Image.
            // Should not cause a memory leak as this is a new descriptor,
            // and equivalent descriptors should overwrite existing entries.
            descriptorTextMap.put(descriptor, getIconString(path));
        }

        return descriptor;
    }

    public Image getImage(URL path)
    {
        return getImage(getImageDescriptor(path));
    }

    public ImageDescriptor getImageDescriptor(QueryDescriptor query)
    {
        URL url = query != null ? query.getIcon() : null;
        return url != null ? getImageDescriptor(url) : null;
    }

    public Image getImage(QueryDescriptor query)
    {
        ImageDescriptor imageDescriptor = getImageDescriptor(query);
        return imageDescriptor == null ? null : getImage(imageDescriptor);
    }

    /**
     * @param url
     *            URL of image file for which a description is required.
     * @return String with meaningful description of image given by input url.
     */
    private String getIconString(URL url)
    {
        // Delegate lookup based on path element of URL.
        return getIconString(url.getPath());
    }

    /**
     * @param path
     *            String representing the path to an image file for which a
     *            description is needed.
     * @return String with meaningful description of image located at input
     *         path. NLS enabled as the string is obtained from a properties
     *         file.
     */
    private String getIconString(String path)
    {
        // Construct system independent string representing path below "icons"
        // This is then used to map to a NLS enabled textual description of the
        // image.
        File imageFile = new File(path); // Full path
        String[] iconPath = parseIconPath(imageFile); // Split into elements
        String iconKey = buildIconKey(iconPath); // Construct key for property
        String iconLabel = IconLabels.getString(iconKey); // Obtain NLS value
        return iconLabel;
    }

    /**
     * @param imageFile
     *            File representing the path to the image. This is converted
     *            into a String[] by splitting the path into elements below the
     *            /icons directory and stripping off the suffix. Returns null if
     *            the file is not below an /icons directory.
     * @return String[] representing the path split into elements as above.
     */
    private static String[] parseIconPath(File imageFile)
    {
        String[] iconPath = null; // Initial and default value to return.
        ArrayList<String> pathList = new ArrayList<String>(); // Accumulator
        // Strip off file suffix.
        pathList.add(imageFile.getName().split("\\.")[0]); //$NON-NLS-1$

        // Iterate backwards up the path, inserting the directory names at the
        // front of the ArrayList.
        // This results in a sequence matching the original order of the path.
        // Do not include the common parent directory "/icons" or ancestors.
        while (imageFile != null)
        { // iterate up the path
            imageFile = imageFile.getParentFile();
            if (imageFile != null) // There was a parent to include.
            {
                String fileName = imageFile.getName();
                if (fileName.equals("icons")) // Iteration complete. //$NON-NLS-1$
                { // Convert ArrayList to array for return.
                    iconPath = pathList.toArray(new String[0]);
                    imageFile = null; // terminate loop
                }
                else
                { // More to do - prepend the name of parent to sequence.
                    pathList.add(0, fileName); // add parent to front of list
                }
            }
        }
        return iconPath; // Return parsed path, or null if unexpected error.
    }

    /**
     * @param iconPath
     *            String[] representing path to icon file below /icons
     * @return String A mangled version of the path with path separators
     *         replaced with '-' to use as a key into the properties file
     *         containing the textual descriptions of the icons. This utility is
     *         used offline to build the properties file, and at runtime to look
     *         up the icon labels from the NLS properties file(s).
     */
    private static String buildIconKey(String[] iconPath)
    {
        if (iconPath == null)
            return IconLabels.UNKNOWN_ICON_KEY;
        // Initialize key with common prefix from IconLabels class.
        StringBuffer propertyBuf = new StringBuffer(IconLabels.ICON_KEY_PREFIX);
        // Iterate through iconPath appending each element after '-'
        for (String pathStr : iconPath)
        {
            propertyBuf.append('-');
            propertyBuf.append(pathStr);
        }
        return propertyBuf.toString(); // Return constructed key.
    }

    /**
     * @param image
     *            The Image for which descriptive text is to be retrieved.
     * @return Descriptive text for the Image object, retrieved from
     *         imageTextMap, or text indicating "unknown image" if not found.
     */
    public String getImageText(Image image)
    {
        String text = imageTextMap.get(image); // May be null
        // Return default string if image not in map.
        return (text == null) ? IconLabels.getString(IconLabels.UNKNOWN_ICON_KEY) : text;
    }

    public IExtensionTracker getExtensionTracker()
    {
        return tracker;
    }

    // //////////////////////////////////////////////////////////////
    // logging
    // //////////////////////////////////////////////////////////////

    public static void log(IStatus status)
    {
        getDefault().getLog().log(status);
    }

    public static void log(Throwable e)
    {
        log(e, Messages.MemoryAnalyserPlugin_InternalError);
    }

    public static void log(Throwable e, String message)
    {
        log(new Status(IStatus.ERROR, PLUGIN_ID, message, e));
    }

    // ///////////////////////////////////////////////////////////////////
    // Main program and associated methods (all offline code)
    // to generate icon label properties from icon file names.
    // Not used in MAT runtime environment.
    // Offline code to generate English labels for icons.
    // The standard required for error handling robustness in this code is
    // lower than would be expected for MAT runtime code, as it is run only
    // as an offline generation utility (by MAT developers only, not by users).
    // ///////////////////////////////////////////////////////////////////

    /**
     * @param args
     *            Input arguments are ignored. This Java program generates a
     *            properties file "iconlabels.properties" automatically, based
     *            on the content of the MAT icons directories. The locations of
     *            the /icons directories are hardcoded to be those under
     *            org.eclipse.mat.api and org.eclipse.mat.ui, relative to the
     *            current working directory which is assumed to be a project in
     *            the workspace. This is the case if this program is
     *            "Run as Java Application" within Eclipse, using the default
     *            working directory org.eclipse.mat.ui. The output is written to
     *            "iconlabels.properties" in the current working directory,
     *            which can then be copied to the required location for the
     *            properties file, org.eclipse.mat.ui/src/org/eclipse/mat/ui/.
     *            Error handling is coarse-grained: any Exception is caught and
     *            details are printed to System.out. Some other diagnostics are
     *            written to System.out if errors occur.
     */
    @SuppressWarnings("nls")
    public static void main(String[] args)
    {
        /*
         * Note that the file output uses \r\n as the line separator, as this is
         * required by the IBM NLS translation tools. This corresponds to 0x0D0A
         * in ASCII/UTF-8 encoding. Hence explicit line separators of \r\n are
         * used throughout the generator code.
         */
        // File for output properties:
        final String propsFilename = "icon_labels.properties";
        // Header string required for translation tooling.
        final String nlsHeaders = "# NLS_MESSAGEFORMAT_NONE\r\n" + "# NLS_ENCODING=UNICODE\r\n" + "# \r\n";
        // Header comment for generated properties file, referring back to this
        // program.
        final String autoComment = "# This file is automatically generated by org.eclipse.mat.ui.MemoryAnalyserPlugin.main().\r\n"
                        + "# Refer to the documentation/comments for this method for usage instructions.\r\n"
                        + "# \r\n"
                        + "# Any manual modifications to this file will need to be reapplied if the file is regenerated.\r\n"
                        + "# Therefore it is preferable if such modifications are kept to a minimum, or preferably\r\n"
                        + "# achieved by amending the label generation code in MemoryAnalyserPlugin.buildIconLabel().\r\n"
                        + "# \r\n";
        // String to include special property to denote the "unknown icon"
        // value.
        final String unknownIconProperty = "# Icon label property to be used for an unknown icon:\r\n"
                        + IconLabels.UNKNOWN_ICON_KEY + "=" + buildIconLabel(null) + "\r\n";
        // String to indicate start of auto-generated label properties.
        final String autoIconsComment = "# Automatically generated icon label properties:";
        final String[] iconDirs = { // UI draws icons from several locations
        // Assume the following two top-level icons directories relative to
        // current directory.
                        "../org.eclipse.mat.api/META-INF/icons", "../org.eclipse.mat.ui/icons", "../org.eclipse.mat.jdt/icons" };
        try
        // Trap any Exceptions at the outermost level.
        {
            // Use a sorted map for the properties so that the ordering is
            // reproducible.
            Map<String, String> iconMap = new TreeMap<String, String>();
            for (String iconDir : iconDirs) // For each /icons directory
                                            // (currently 3).
            {
                File iconDirFile = new File(iconDir);
                if (iconDirFile.isDirectory()) // Check input is valid directory
                { // Generate properties for the directory and add to map.
                    generateIconProps(iconDirFile, iconMap);
                }
                else
                { // Error case - report to user.
                    System.out.println("Input is not a directory: " + iconDir);
                }

            }
            // Now write out iconlabels.properties
            File iconLabelsFile = new File(propsFilename);
            PrintStream iconLabelsStream = null;
            // Properties files are always encoded in ISO-8859-1
            iconLabelsStream = new PrintStream(new FileOutputStream(iconLabelsFile), false, "ISO-8859-1");
            // Print NLS headers required for translation
            // Use printPropertyLine() to insert \r\n separator.
            printPropertyLine(iconLabelsStream, nlsHeaders);
            // Print header comment referring to this generator code.
            printPropertyLine(iconLabelsStream, autoComment);
            // Print special label to be used for unknown icons.
            printPropertyLine(iconLabelsStream, unknownIconProperty);
            // Print special label to be used for unknown icons.
            printPropertyLine(iconLabelsStream, autoIconsComment);
            // Print out iconMap entries, in collation sequence for
            // reproducibility.
            for (Entry<String, String> mapEntry : iconMap.entrySet())
            {
                iconLabelsStream.print(mapEntry.getKey());
                iconLabelsStream.print('=');
                printPropertyLine(iconLabelsStream, mapEntry.getValue());
            }
            iconLabelsStream.close();
            // Print completion message to console
            System.out.println("Icon label properties written to file: " + iconLabelsFile.getAbsolutePath());
        }
        catch (Exception e) // Catch all exceptions and print details to
                            // System.out
        {
            System.out.println(e.toString());
            e.printStackTrace(System.out);
        }
    }

    /**
     * @param stream
     *            PrintStream to print line to.
     * @param line
     *            String to append to PrintStream. Writes line to stream (just
     *            like println()) but using "\r\n" as line terminator to satisfy
     *            requirements of IBM translation tools.
     */
    private static void printPropertyLine(PrintStream stream, String line)
    {
        stream.print(line);
        stream.print("\r\n"); // Use MS-DOS line termination (0D0A) for
    }

    /**
     * @param iconDir
     *            File representing a directory to search for icon files and to
     *            add the generated key/value pairs to iconMap.
     * @param iconMap
     *            Map to add the generated key/value pairs for the icons. This
     *            should be a sorted Map to ensure reproducible ordering of
     *            output. Recursively invoked method to write out generated
     *            labels for icons in the form of a properties file. The
     *            recursion excludes hidden files & directories
     */
    private static void generateIconProps(File iconDir, Map<String, String> iconMap)
    { // precondition: iconDir is a directory.
        File[] fileList = iconDir.listFiles(); // Should not be null, may be
                                               // empty.
        for (File file : fileList) // Iterate over directory contents.
        {
            if (!file.isHidden())
            { // Exclude hidden files eg .svn directories
                if (file.isDirectory())
                { // Directory, so recurse into child directory.
                    generateIconProps(file, iconMap);
                }
                else
                { // File, so add key/value pair to Map.
                    String[] iconPath = parseIconPath(file);
                    String key = buildIconKey(iconPath);
                    String label = buildIconLabel(iconPath);
                    iconMap.put(key, label); // Duplicate key will overwrite
                }
            } // if()
        }
    }

    /**
     * @param iconPath
     *            path of icon below "/icons", without file qualifier(s), parsed
     *            into String[] by
     * @return We simply return a line representing a textual description for
     *         the icon, based on it's file location & name. This automated
     *         process can be adjusted to produce any desired result, provided
     *         the resulting line is a valid Java property value. Performance is
     *         not important as this is non-runtime code. It's more important
     *         that the results should be readily tailorable.
     */
    @SuppressWarnings("nls")
    private static String buildIconLabel(String[] iconPath)
    {
        // Input is path of icon below "/icons", without file qualifier(s).
        if (iconPath == null) // Invalid path, icon may not exist.
            return "unknown icon"; // Special case label.
        StringBuffer labelBuf = new StringBuffer(); // Initially empty
        // Split base file name of icon into tokens delimited by '_'
        String[] iconName = iconPath[iconPath.length - 1].split("_");
        for (String nameElem : iconName)
        { // For each component token, perform required tailoring.
          // Expand common abbreviations
            if (nameElem.equals("obj"))
                nameElem = "object";
            if (nameElem.equals("frgmt"))
                nameElem = "fragment";
            if (nameElem.equals("frgmts"))
                nameElem = "fragments";
            if (nameElem.equals("attr"))
                nameElem = "attribute";
            if (nameElem.equals("ext"))
                nameElem = "extension";
            if (nameElem.equals("mpaths"))
                nameElem = "merge paths";
            // Add to buffer
            labelBuf.append(nameElem);
            labelBuf.append(' '); // Add space between elements
        }
        // Iterate backwards up path, adding parent directory names to text.
        for (int ipath = iconPath.length - 2; ipath >= 1; ipath--)
        { // omit top-level directory if there is one (index 0 not included).
            labelBuf.append(iconPath[ipath]);
            labelBuf.append(' '); // Appends trailing blank which is OK for
                                  // properties
        }
        return labelBuf.toString(); // Convert to String and return.
    }

}

Back to the top