diff -r 055826baee46 openide.explorer/src/org/openide/explorer/view/VisualizerNode.java --- a/openide.explorer/src/org/openide/explorer/view/VisualizerNode.java Fri Jun 13 10:15:48 2008 +0200 +++ b/openide.explorer/src/org/openide/explorer/view/VisualizerNode.java Fri Jun 13 12:59:38 2008 +0200 @@ -56,10 +56,10 @@ import java.util.*; import javax.swing.Icon; -import javax.swing.ImageIcon; import javax.swing.SwingUtilities; import javax.swing.event.EventListenerList; import javax.swing.tree.TreeNode; +import org.openide.util.ImageUtilities; /** Visual representation of one node. Holds necessary information about nodes @@ -206,7 +206,14 @@ if (desc == UNKNOWN) { shortDescription = desc = node.getShortDescription(); } - + if (icon instanceof Image) { + String toolTip = ImageUtilities.getImageToolTip((Image) icon); + if (toolTip.length() > 0) { + StringBuilder str = new StringBuilder(128); + str.append("").append(desc).append("
").append(toolTip).append(""); + desc = str.toString(); + } + } return desc; } @@ -407,7 +414,7 @@ if (Node.PROP_NAME.equals(name) || Node.PROP_DISPLAY_NAME.equals(name) || isIconChange) { if (isIconChange) { //Ditch the cached icon type so the next call to getIcon() will - //recreate the ImageIcon + //recreate the Icon cachedIconType = -1; } @@ -558,7 +565,7 @@ icon = getDefaultIcon(); } else { - icon = new ImageIcon(image); + icon = ImageUtilities.image2Icon(image); } } @@ -577,9 +584,8 @@ /** Loads default icon if not loaded. */ private static Icon getDefaultIcon() { if (defaultIcon == null) { - defaultIcon = new ImageIcon(Utilities.loadImage(DEFAULT_ICON)); + defaultIcon = ImageUtilities.image2Icon(ImageUtilities.loadImage(DEFAULT_ICON)); } - return defaultIcon; } diff -r 055826baee46 openide.explorer/test/unit/src/org/openide/explorer/view/VisualizerNodeTest.java --- a/openide.explorer/test/unit/src/org/openide/explorer/view/VisualizerNodeTest.java Fri Jun 13 10:15:48 2008 +0200 +++ b/openide.explorer/test/unit/src/org/openide/explorer/view/VisualizerNodeTest.java Fri Jun 13 12:59:38 2008 +0200 @@ -89,4 +89,16 @@ assertEquals("Index is 1", 1, ta.getIndex(tm)); } + + public void testIconsAreShared() { + AbstractNode a1 = new AbstractNode(Children.LEAF); + VisualizerNode v1 = VisualizerNode.getVisualizer(null, a1); + Icon icon1 = v1.getIcon(false, false); + + AbstractNode a2 = new AbstractNode(Children.LEAF); + VisualizerNode v2 = VisualizerNode.getVisualizer(null, a2); + Icon icon2 = v2.getIcon(false, false); + + assertSame("Icon instances should be same", icon1, icon2); + } } diff -r 055826baee46 openide.util/apichanges.xml --- a/openide.util/apichanges.xml Fri Jun 13 10:15:48 2008 +0200 +++ b/openide.util/apichanges.xml Fri Jun 13 12:59:38 2008 +0200 @@ -49,6 +49,38 @@ Actions API + + + Image methods were made deprecated in Utilities, replacement is ImageUtilities (with additional methods). + + + + + +

+ Image methods were made deprecated in Utilities. Replacement is + ImageUtilities (renamed IconManager). It contains also new + methods for image tool tips manipulation: + New methods for tool tips manipulation: + Image assignToolTipToImage(Image image, String text); + String getImageToolTip(Image image); + Image addToolTipToImage(Image image, String text); +

+

+ New method for conversion from Image to Icon: + Icon image2Icon(Image image); +

+

+ Deprecated methods in Utilities: + Image icon2Image(Icon icon); + Image loadImage(String resourceID); + Image loadImage(String resource, boolean localized); + Image mergeImages(Image image1, Image image2, int x, int y); +

+
+ + +
Mutex made pluggable diff -r 055826baee46 openide.util/nbproject/project.properties --- a/openide.util/nbproject/project.properties Fri Jun 13 10:15:48 2008 +0200 +++ b/openide.util/nbproject/project.properties Fri Jun 13 12:59:38 2008 +0200 @@ -41,7 +41,7 @@ javac.source=1.5 module.jar.dir=lib -spec.version.base=7.13.0 +spec.version.base=7.14.0 # For XMLSerializer, needed for XMLUtil.write to work w/ namespaces under JDK 1.4: diff -r 055826baee46 openide.util/src/org/openide/util/IconManager.java --- a/openide.util/src/org/openide/util/IconManager.java Fri Jun 13 10:15:48 2008 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,517 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common - * Development and Distribution License("CDDL") (collectively, the - * "License"). You may not use this file except in compliance with the - * License. You can obtain a copy of the License at - * http://www.netbeans.org/cddl-gplv2.html - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the - * specific language governing permissions and limitations under the - * License. When distributing the software, include this License Header - * Notice in each file and include the License file at - * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the GPL Version 2 section of the License file that - * accompanied this code. If applicable, add the following below the - * License Header, with the fields enclosed by brackets [] replaced by - * your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * - * Contributor(s): - * - * The Original Software is NetBeans. The Initial Developer of the Original - * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun - * Microsystems, Inc. All Rights Reserved. - * - * If you wish your version of this file to be governed by only the CDDL - * or only the GPL Version 2, indicate your decision by adding - * "[Contributor] elects to include this software in this distribution - * under the [CDDL or GPL Version 2] license." If you do not indicate a - * single choice of license, a recipient has the option to distribute - * your version of this file under either the CDDL, the GPL Version 2 or - * to extend the choice of license to its licensees as provided above. - * However, if you add GPL Version 2 code and therefore, elected the GPL - * Version 2 license, then the option applies only if the new code is - * made subject to such option by the copyright holder. - */ - -package org.openide.util; - -import java.awt.Component; -import java.awt.HeadlessException; -import java.awt.Image; -import java.awt.MediaTracker; -import java.awt.Toolkit; -import java.awt.Transparency; -import java.awt.image.BufferedImage; -import java.awt.image.ColorModel; -import java.awt.image.ImageObserver; -import java.io.IOException; -import java.lang.ref.SoftReference; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.imageio.ImageIO; -import javax.imageio.ImageReadParam; -import javax.imageio.ImageReader; -import javax.imageio.stream.ImageInputStream; - -/** Registers all loaded images into the AbstractNode, so nothing is loaded twice. -* -* @author Jaroslav Tulach -*/ -final class IconManager extends Object { - /** a value that indicates that the icon does not exists */ - private static final ActiveRef NO_ICON = new ActiveRef(null, null, null); - - private static final Map> cache = new HashMap>(128); - private static final Map> localizedCache = new HashMap>(128); - private static final Map> compositeCache = new HashMap>(128); - - /** Resource paths for which we have had to strip initial slash. - * @see "#20072" - */ - private static final Set extraInitialSlashes = new HashSet(); - private static volatile Object currentLoader; - private static Lookup.Result loaderQuery = null; - private static boolean noLoaderWarned = false; - private static final Component component = new Component() { - }; - - private static final MediaTracker tracker = new MediaTracker(component); - private static int mediaTrackerID; - - private static ImageReader PNG_READER; -// private static ImageReader GIF_READER; - - private static final Logger ERR = Logger.getLogger(IconManager.class.getName()); - - static { - ImageIO.setUseCache(false); - PNG_READER = ImageIO.getImageReadersByMIMEType("image/png").next(); -// GIF_READER = ImageIO.getImageReadersByMIMEType("image/gif").next(); - } - - /** - * Get the class loader from lookup. - * Since this is done very frequently, it is wasteful to query lookup each time. - * Instead, remember the last result and just listen for changes. - */ - static ClassLoader getLoader() { - Object is = currentLoader; - if (is instanceof ClassLoader) { - return (ClassLoader)is; - } - - currentLoader = Thread.currentThread(); - - if (loaderQuery == null) { - loaderQuery = Lookup.getDefault().lookup(new Lookup.Template(ClassLoader.class)); - loaderQuery.addLookupListener( - new LookupListener() { - public void resultChanged(LookupEvent ev) { - ERR.fine("Loader cleared"); // NOI18N - currentLoader = null; - } - } - ); - } - - Iterator it = loaderQuery.allInstances().iterator(); - if (it.hasNext()) { - ClassLoader toReturn = (ClassLoader) it.next(); - if (currentLoader == Thread.currentThread()) { - currentLoader = toReturn; - } - ERR.fine("Loader computed: " + currentLoader); // NOI18N - return toReturn; - } else { if (!noLoaderWarned) { - noLoaderWarned = true; - ERR.warning( - "No ClassLoader instance found in " + Lookup.getDefault() // NOI18N - ); - } - return null; - } - } - - static Image getIcon(String resource, boolean localized) { - if (localized) { - synchronized (localizedCache) { - ActiveRef ref = localizedCache.get(resource); - Image img = null; - - // no icon for this name (already tested) - if (ref == NO_ICON) { - return null; - } - - if (ref != null) { - // then it is SoftRefrence - img = ref.get(); - } - - // icon found - if (img != null) { - return img; - } - - // find localized or base image - ClassLoader loader = getLoader(); - - // we'll keep the String probably for long time, optimize it - resource = new String(resource).intern(); // NOPMD - - String base; - String ext; - int idx = resource.lastIndexOf('.'); - - if ((idx != -1) && (idx > resource.lastIndexOf('/'))) { - base = resource.substring(0, idx); - ext = resource.substring(idx); - } else { - base = resource; - ext = ""; // NOI18N - } - - // #31008. [PENDING] remove in case package cache is precomputed - java.net.URL baseurl = (loader != null) ? loader.getResource(resource) // NOPMD - : IconManager.class.getClassLoader().getResource(resource); - Iterator it = NbBundle.getLocalizingSuffixes(); - - while (it.hasNext()) { - String suffix = it.next(); - Image i; - - if (suffix.length() == 0) { - i = getIcon(resource, loader, false); - } else { - i = getIcon(base + suffix + ext, loader, true); - } - - if (i != null) { - localizedCache.put(resource, new ActiveRef(i, localizedCache, resource)); - - return i; - } - } - - localizedCache.put(resource, NO_ICON); - - return null; - } - } else { - return getIcon(resource, getLoader(), false); - } - } - - /** Finds image for given resource. - * @param name name of the resource - * @param loader classloader to use for locating it, or null to use classpath - * @param localizedQuery whether the name contains some localization suffix - * and is not optimized/interned - */ - private static Image getIcon(String name, ClassLoader loader, boolean localizedQuery) { - ActiveRef ref = cache.get(name); - Image img = null; - - // no icon for this name (already tested) - if (ref == NO_ICON) { - return null; - } - - if (ref != null) { - img = ref.get(); - } - - // icon found - if (img != null) { - return img; - } - - synchronized (cache) { - // again under the lock - ref = cache.get(name); - - // no icon for this name (already tested) - if (ref == NO_ICON) { - return null; - } - - if (ref != null) { - // then it is SoftRefrence - img = ref.get(); - } - - if (img != null) { - // cannot be NO_ICON, since it never disappears from the map. - return img; - } - - // path for bug in classloader - String n; - boolean warn; - - if (name.startsWith("/")) { // NOI18N - warn = true; - n = name.substring(1); - } else { - warn = false; - n = name; - } - - // we have to load it - java.net.URL url = (loader != null) ? loader.getResource(n) - : IconManager.class.getClassLoader().getResource(n); - -// img = (url == null) ? null : Toolkit.getDefaultToolkit().createImage(url); - Image result = null; - try { - if (url != null) { - if (name.endsWith(".png")) { - ImageInputStream stream = ImageIO.createImageInputStream(url.openStream()); - ImageReadParam param = PNG_READER.getDefaultReadParam(); - try { - PNG_READER.setInput(stream, true, true); - result = PNG_READER.read(0, param); - } - catch (IOException ioe1) { - ERR.log(Level.INFO, "Image "+name+" is not PNG", ioe1); - } - stream.close(); - } - /* - else if (name.endsWith(".gif")) { - ImageInputStream stream = ImageIO.createImageInputStream(url.openStream()); - ImageReadParam param = GIF_READER.getDefaultReadParam(); - try { - GIF_READER.setInput(stream, true, true); - result = GIF_READER.read(0, param); - } - catch (IOException ioe1) { - ERR.log(Level.INFO, "Image "+name+" is not GIF", ioe1); - } - stream.close(); - } - */ - - if (result == null) { - result = ImageIO.read(url); - } - } - } catch (IOException ioe) { - ERR.log(Level.WARNING, "Cannot load " + name + " image", ioe); - } - - if (result != null) { - if (warn && extraInitialSlashes.add(name)) { - ERR.warning( - "Initial slashes in Utilities.loadImage deprecated (cf. #20072): " + - name - ); // NOI18N - } - -// Image img2 = toBufferedImage(result); - - ERR.log(Level.FINE, - "loading icon {0} = {1}", new Object[] {n, result}); - name = new String(name).intern(); // NOPMD - - cache.put(name, new ActiveRef(result, cache, name)); - - return result; - } else { // no icon found - - if (!localizedQuery) { - cache.put(name, NO_ICON); - } - - return null; - } - } - } - - /** - * Method that attempts to find the merged image in the cache first, then - * creates the image if it was not found. - */ - static final Image mergeImages(Image im1, Image im2, int x, int y) { - CompositeImageKey k = new CompositeImageKey(im1, im2, x, y); - Image cached; - - synchronized (compositeCache) { - ActiveRef r = compositeCache.get(k); - - if (r != null) { - cached = r.get(); - - if (cached != null) { - return cached; - } - } - - cached = doMergeImages(im1, im2, x, y); - compositeCache.put(k, new ActiveRef(cached, compositeCache, k)); - - return cached; - } - } - - /** The method creates a BufferedImage which represents the same Image as the - * parameter but consumes less memory. - */ - static final Image toBufferedImage(Image img) { - // load the image - new javax.swing.ImageIcon(img, ""); - - if (img.getHeight(null)*img.getWidth(null) > 24*24) { - return img; - } - java.awt.image.BufferedImage rep = createBufferedImage(img.getWidth(null), img.getHeight(null)); - java.awt.Graphics g = rep.createGraphics(); - g.drawImage(img, 0, 0, null); - g.dispose(); - img.flush(); - - return rep; - } - - private static void ensureLoaded(Image image) { - if ( - (Toolkit.getDefaultToolkit().checkImage(image, -1, -1, null) & - (ImageObserver.ALLBITS | ImageObserver.FRAMEBITS)) != 0 - ) { - return; - } - - synchronized (tracker) { - int id = ++mediaTrackerID; - - tracker.addImage(image, id); - - try { - tracker.waitForID(id, 0); - } catch (InterruptedException e) { - System.out.println("INTERRUPTED while loading Image"); - } - - assert (tracker.statusID(id, false) == MediaTracker.COMPLETE) : "Image loaded"; - tracker.removeImage(image, id); - } - } - - private static final Image doMergeImages(Image image1, Image image2, int x, int y) { - ensureLoaded(image1); - ensureLoaded(image2); - - int w = Math.max(image1.getWidth(null), x + image2.getWidth(null)); - int h = Math.max(image1.getHeight(null), y + image2.getHeight(null)); - boolean bitmask = (image1 instanceof Transparency) && ((Transparency)image1).getTransparency() != Transparency.TRANSLUCENT - && (image2 instanceof Transparency) && ((Transparency)image2).getTransparency() != Transparency.TRANSLUCENT; - - ColorModel model = colorModel(bitmask? Transparency.BITMASK: Transparency.TRANSLUCENT); - java.awt.image.BufferedImage buffImage = new java.awt.image.BufferedImage( - model, model.createCompatibleWritableRaster(w, h), model.isAlphaPremultiplied(), null - ); - - java.awt.Graphics g = buffImage.createGraphics(); - g.drawImage(image1, 0, 0, null); - g.drawImage(image2, x, y, null); - g.dispose(); - - return buffImage; - } - - /** Creates BufferedImage with Transparency.TRANSLUCENT */ - static final java.awt.image.BufferedImage createBufferedImage(int width, int height) { - if (Utilities.isMac()) { - return new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE); - } - - ColorModel model = colorModel(java.awt.Transparency.TRANSLUCENT); - java.awt.image.BufferedImage buffImage = new java.awt.image.BufferedImage( - model, model.createCompatibleWritableRaster(width, height), model.isAlphaPremultiplied(), null - ); - - return buffImage; - } - - static private ColorModel colorModel(int transparency) { - ColorModel model; - try { - model = java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment() - .getDefaultScreenDevice().getDefaultConfiguration() - .getColorModel(transparency); - } - catch(HeadlessException he) { - model = ColorModel.getRGBdefault(); - } - - return model; - } - - /** - * Key used for composite images -- it holds image identities - */ - private static class CompositeImageKey { - Image baseImage; - Image overlayImage; - int x; - int y; - - CompositeImageKey(Image base, Image overlay, int x, int y) { - this.x = x; - this.y = y; - this.baseImage = base; - this.overlayImage = overlay; - } - - public boolean equals(Object other) { - if (!(other instanceof CompositeImageKey)) { - return false; - } - - CompositeImageKey k = (CompositeImageKey) other; - - return (x == k.x) && (y == k.y) && (baseImage == k.baseImage) && (overlayImage == k.overlayImage); - } - - public int hashCode() { - int hash = ((x << 3) ^ y) << 4; - hash = hash ^ baseImage.hashCode() ^ overlayImage.hashCode(); - - return hash; - } - - public String toString() { - return "Composite key for " + baseImage + " + " + overlayImage + " at [" + x + ", " + y + "]"; // NOI18N - } - } - - /** Cleaning reference. */ - private static final class ActiveRef extends SoftReference implements Runnable { - private Map> holder; - private T key; - - public ActiveRef(Image o, Map> holder, T key) { - super(o, Utilities.activeReferenceQueue()); - this.holder = holder; - this.key = key; - } - - public void run() { - synchronized (holder) { - holder.remove(key); - } - } - } - // end of ActiveRef -} diff -r 055826baee46 openide.util/src/org/openide/util/ImageUtilities.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/openide.util/src/org/openide/util/ImageUtilities.java Fri Jun 13 12:59:38 2008 +0200 @@ -0,0 +1,738 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ + +package org.openide.util; + +import java.awt.Component; +import java.awt.Graphics; +import java.awt.HeadlessException; +import java.awt.Image; +import java.awt.MediaTracker; +import java.awt.Toolkit; +import java.awt.Transparency; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.IndexColorModel; +import java.awt.image.WritableRaster; +import java.io.IOException; +import java.lang.ref.SoftReference; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.imageio.ImageIO; +import javax.imageio.ImageReadParam; +import javax.imageio.ImageReader; +import javax.imageio.stream.ImageInputStream; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JLabel; + +/** + * Useful static methods for manipulation with images/icons, results are cached. + * + * @author Jaroslav Tulach + * @since 7.14 + */ +public final class ImageUtilities { + /** separator for individual parts of tool tip text */ + static final String TOOLTIP_SEPAR = "
"; // NOI18N + /** a value that indicates that the icon does not exists */ + private static final ActiveRef NO_ICON = new ActiveRef(null, null, null); + + private static final Map> cache = new HashMap>(128); + private static final Map> localizedCache = new HashMap>(128); + private static final Map> compositeCache = new HashMap>(128); + private static final Map> imageToolTipCache = new HashMap>(128); + + /** Resource paths for which we have had to strip initial slash. + * @see "#20072" + */ + private static final Set extraInitialSlashes = new HashSet(); + private static volatile Object currentLoader; + private static Lookup.Result loaderQuery = null; + private static boolean noLoaderWarned = false; + private static final Component component = new Component() { + }; + + private static final MediaTracker tracker = new MediaTracker(component); + private static int mediaTrackerID; + + private static ImageReader PNG_READER; +// private static ImageReader GIF_READER; + + private static final Logger ERR = Logger.getLogger(ImageUtilities.class.getName()); + + static { + ImageIO.setUseCache(false); + PNG_READER = ImageIO.getImageReadersByMIMEType("image/png").next(); +// GIF_READER = ImageIO.getImageReadersByMIMEType("image/gif").next(); + } + + /** + * Loads an image from the specified resource ID. The image is loaded using the "system" classloader registered in + * Lookup. + * @param resourceID resource path of the icon (no initial slash) + * @return icon's Image, or null, if the icon cannot be loaded. + */ + public static final Image loadImage(String resourceID) { + return getIcon(resourceID, false); + } + + /** + * Loads an image based on resource path. + * Exactly like {@link #loadImage(String)} but may do a localized search. + * For example, requesting org/netbeans/modules/foo/resources/foo.gif + * might actually find org/netbeans/modules/foo/resources/foo_ja.gif + * or org/netbeans/modules/foo/resources/foo_mybranding.gif. + * + *

Caching of loaded images can be used internally to improve performance. + * + */ + public static final Image loadImage(String resource, boolean localized) { + return getIcon(resource, localized); + } + + /** This method merges two images into the new one. The second image is drawn + * over the first one with its top-left corner at x, y. Images need not be of the same size. + * New image will have a size of max(second image size + top-left corner, first image size). + * Method is used mostly when second image contains transparent pixels (e.g. for badging). + * Method that attempts to find the merged image in the cache first, then + * creates the image if it was not found. + * @param image1 underlying image + * @param image2 second image + * @param x x position of top-left corner + * @param y y position of top-left corner + * @return new merged image + */ + public static final Image mergeImages(Image image1, Image image2, int x, int y) { + if (image1 == null || image2 == null) { + throw new NullPointerException(); + } + + CompositeImageKey k = new CompositeImageKey(image1, image2, x, y); + Image cached; + + synchronized (compositeCache) { + ActiveRef r = compositeCache.get(k); + if (r != null) { + cached = r.get(); + if (cached != null) { + return cached; + } + } + cached = doMergeImages(image1, image2, x, y); + compositeCache.put(k, new ActiveRef(cached, compositeCache, k)); + return cached; + } + } + + /** + * Converts given image to a {@link javax.swing.Icon}. + * @param image {@link java.awt.Image} to be converted. + * @return icon corresponding {@link javax.swing.Icon} + */ + public static final Icon image2Icon(Image image) { + if (image instanceof ToolTipImage) { + return (Icon) image; + } else { + return new ImageIcon(image); + } + } + + /** + * Converts given icon to a {@link java.awt.Image}. + * + * @param icon {@link javax.swing.Icon} to be converted. + */ + public static final Image icon2Image(Icon icon) { + if (icon instanceof ToolTipImage) { + return (Image) icon; + } else { + ToolTipImage image = new ToolTipImage("", icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB); + Graphics g = image.getGraphics(); + icon.paintIcon(new JLabel(), g, 0, 0); + g.dispose(); + return image; + } + } + + /** + * Assign tool tip text to given image (creates new or returns cached, original remains unmodified) + * Text can contain HTML tags e.g. "<b>my</b> text" + * @param image image to which tool tip should be set + * @param text tool tip text + * @return Image with attached tool tip + */ + public static final Image assignToolTipToImage(Image image, String text) { + ToolTipImageKey key = new ToolTipImageKey(image, text); + Image cached; + synchronized (imageToolTipCache) { + ActiveRef r = imageToolTipCache.get(key); + if (r != null) { + cached = r.get(); + if (cached != null) { + return cached; + } + } + cached = ToolTipImage.createNew(text, image); + imageToolTipCache.put(key, new ActiveRef(cached, imageToolTipCache, key)); + return cached; + } + } + + /** + * Get tool tip text for given image + * @param image image which is asked for tool tip text + * @return String containing attached tool tip text + */ + public static final String getImageToolTip(Image image) { + if (image instanceof ToolTipImage) { + return ((ToolTipImage) image).toolTipText; + } else { + return ""; + } + } + + /** + * Add text to tool tip for given image (creates new or returns cached, original remains unmodified) + * Text can contain HTML tags e.g. "<b>my</b> text" + * @param text text to add to tool tip + * @return Image with attached tool tip + */ + public static final Image addToolTipToImage(Image image, String text) { + if (image instanceof ToolTipImage) { + ToolTipImage tti = (ToolTipImage) image; + StringBuilder str = new StringBuilder(tti.toolTipText); + if (str.length() > 0 && text.length() > 0) { + str.append(TOOLTIP_SEPAR); + } + str.append(text); + return assignToolTipToImage(image, str.toString()); + } else { + return assignToolTipToImage(image, text); + } + } + + /** + * Get the class loader from lookup. + * Since this is done very frequently, it is wasteful to query lookup each time. + * Instead, remember the last result and just listen for changes. + */ + static ClassLoader getLoader() { + Object is = currentLoader; + if (is instanceof ClassLoader) { + return (ClassLoader)is; + } + + currentLoader = Thread.currentThread(); + + if (loaderQuery == null) { + loaderQuery = Lookup.getDefault().lookup(new Lookup.Template(ClassLoader.class)); + loaderQuery.addLookupListener( + new LookupListener() { + public void resultChanged(LookupEvent ev) { + ERR.fine("Loader cleared"); // NOI18N + currentLoader = null; + } + } + ); + } + + Iterator it = loaderQuery.allInstances().iterator(); + if (it.hasNext()) { + ClassLoader toReturn = (ClassLoader) it.next(); + if (currentLoader == Thread.currentThread()) { + currentLoader = toReturn; + } + ERR.fine("Loader computed: " + currentLoader); // NOI18N + return toReturn; + } else { if (!noLoaderWarned) { + noLoaderWarned = true; + ERR.warning( + "No ClassLoader instance found in " + Lookup.getDefault() // NOI18N + ); + } + return null; + } + } + + static Image getIcon(String resource, boolean localized) { + if (localized) { + synchronized (localizedCache) { + ActiveRef ref = localizedCache.get(resource); + Image img = null; + + // no icon for this name (already tested) + if (ref == NO_ICON) { + return null; + } + + if (ref != null) { + // then it is SoftRefrence + img = ref.get(); + } + + // icon found + if (img != null) { + return img; + } + + // find localized or base image + ClassLoader loader = getLoader(); + + // we'll keep the String probably for long time, optimize it + resource = new String(resource).intern(); // NOPMD + + String base; + String ext; + int idx = resource.lastIndexOf('.'); + + if ((idx != -1) && (idx > resource.lastIndexOf('/'))) { + base = resource.substring(0, idx); + ext = resource.substring(idx); + } else { + base = resource; + ext = ""; // NOI18N + } + + // #31008. [PENDING] remove in case package cache is precomputed + java.net.URL baseurl = (loader != null) ? loader.getResource(resource) // NOPMD + : ImageUtilities.class.getClassLoader().getResource(resource); + Iterator it = NbBundle.getLocalizingSuffixes(); + + while (it.hasNext()) { + String suffix = it.next(); + Image i; + + if (suffix.length() == 0) { + i = getIcon(resource, loader, false); + } else { + i = getIcon(base + suffix + ext, loader, true); + } + + if (i != null) { + localizedCache.put(resource, new ActiveRef(i, localizedCache, resource)); + return i; + } + } + localizedCache.put(resource, NO_ICON); + return null; + } + } else { + return getIcon(resource, getLoader(), false); + } + } + + /** Finds image for given resource. + * @param name name of the resource + * @param loader classloader to use for locating it, or null to use classpath + * @param localizedQuery whether the name contains some localization suffix + * and is not optimized/interned + */ + private static Image getIcon(String name, ClassLoader loader, boolean localizedQuery) { + ActiveRef ref = cache.get(name); + Image img = null; + + // no icon for this name (already tested) + if (ref == NO_ICON) { + return null; + } + + if (ref != null) { + img = ref.get(); + } + + // icon found + if (img != null) { + return img; + } + + synchronized (cache) { + // again under the lock + ref = cache.get(name); + + // no icon for this name (already tested) + if (ref == NO_ICON) { + return null; + } + + if (ref != null) { + // then it is SoftRefrence + img = ref.get(); + } + + if (img != null) { + // cannot be NO_ICON, since it never disappears from the map. + return img; + } + + // path for bug in classloader + String n; + boolean warn; + + if (name.startsWith("/")) { // NOI18N + warn = true; + n = name.substring(1); + } else { + warn = false; + n = name; + } + + // we have to load it + java.net.URL url = (loader != null) ? loader.getResource(n) + : ImageUtilities.class.getClassLoader().getResource(n); + +// img = (url == null) ? null : Toolkit.getDefaultToolkit().createImage(url); + Image result = null; + try { + if (url != null) { + if (name.endsWith(".png")) { + ImageInputStream stream = ImageIO.createImageInputStream(url.openStream()); + ImageReadParam param = PNG_READER.getDefaultReadParam(); + try { + PNG_READER.setInput(stream, true, true); + result = PNG_READER.read(0, param); + } + catch (IOException ioe1) { + ERR.log(Level.INFO, "Image "+name+" is not PNG", ioe1); + } + stream.close(); + } + /* + else if (name.endsWith(".gif")) { + ImageInputStream stream = ImageIO.createImageInputStream(url.openStream()); + ImageReadParam param = GIF_READER.getDefaultReadParam(); + try { + GIF_READER.setInput(stream, true, true); + result = GIF_READER.read(0, param); + } + catch (IOException ioe1) { + ERR.log(Level.INFO, "Image "+name+" is not GIF", ioe1); + } + stream.close(); + } + */ + + if (result == null) { + result = ImageIO.read(url); + } + } + } catch (IOException ioe) { + ERR.log(Level.WARNING, "Cannot load " + name + " image", ioe); + } + + if (result != null) { + if (warn && extraInitialSlashes.add(name)) { + ERR.warning( + "Initial slashes in Utilities.loadImage deprecated (cf. #20072): " + + name + ); // NOI18N + } + +// Image img2 = toBufferedImage(result); + + ERR.log(Level.FINE, "loading icon {0} = {1}", new Object[] {n, result}); + name = new String(name).intern(); // NOPMD + result = ToolTipImage.createNew("", result); + cache.put(name, new ActiveRef(result, cache, name)); + return result; + } else { // no icon found + if (!localizedQuery) { + cache.put(name, NO_ICON); + } + return null; + } + } + } + + /** The method creates a BufferedImage which represents the same Image as the + * parameter but consumes less memory. + */ + static final Image toBufferedImage(Image img) { + // load the image + new javax.swing.ImageIcon(img, ""); + + if (img.getHeight(null)*img.getWidth(null) > 24*24) { + return img; + } + java.awt.image.BufferedImage rep = createBufferedImage(img.getWidth(null), img.getHeight(null)); + java.awt.Graphics g = rep.createGraphics(); + g.drawImage(img, 0, 0, null); + g.dispose(); + img.flush(); + + return rep; + } + + private static void ensureLoaded(Image image) { + if ( + (Toolkit.getDefaultToolkit().checkImage(image, -1, -1, null) & + (ImageObserver.ALLBITS | ImageObserver.FRAMEBITS)) != 0 + ) { + return; + } + + synchronized (tracker) { + int id = ++mediaTrackerID; + tracker.addImage(image, id); + + try { + tracker.waitForID(id, 0); + } catch (InterruptedException e) { + System.out.println("INTERRUPTED while loading Image"); + } + + assert (tracker.statusID(id, false) == MediaTracker.COMPLETE) : "Image loaded"; + tracker.removeImage(image, id); + } + } + + private static final Image doMergeImages(Image image1, Image image2, int x, int y) { + ensureLoaded(image1); + ensureLoaded(image2); + + int w = Math.max(image1.getWidth(null), x + image2.getWidth(null)); + int h = Math.max(image1.getHeight(null), y + image2.getHeight(null)); + boolean bitmask = (image1 instanceof Transparency) && ((Transparency)image1).getTransparency() != Transparency.TRANSLUCENT + && (image2 instanceof Transparency) && ((Transparency)image2).getTransparency() != Transparency.TRANSLUCENT; + + StringBuilder str = new StringBuilder(image1 instanceof ToolTipImage ? ((ToolTipImage)image1).toolTipText : ""); + if (image2 instanceof ToolTipImage) { + String toolTip = ((ToolTipImage)image2).toolTipText; + if (str.length() > 0 && toolTip.length() > 0) { + str.append(TOOLTIP_SEPAR); + } + str.append(toolTip); + } + + ColorModel model = colorModel(bitmask? Transparency.BITMASK: Transparency.TRANSLUCENT); + ToolTipImage buffImage = new ToolTipImage(str.toString(), + model, model.createCompatibleWritableRaster(w, h), model.isAlphaPremultiplied(), null + ); + + java.awt.Graphics g = buffImage.createGraphics(); + g.drawImage(image1, 0, 0, null); + g.drawImage(image2, x, y, null); + g.dispose(); + + return buffImage; + } + + /** Creates BufferedImage with Transparency.TRANSLUCENT */ + static final java.awt.image.BufferedImage createBufferedImage(int width, int height) { + if (Utilities.isMac()) { + return new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE); + } + + ColorModel model = colorModel(java.awt.Transparency.TRANSLUCENT); + java.awt.image.BufferedImage buffImage = new java.awt.image.BufferedImage( + model, model.createCompatibleWritableRaster(width, height), model.isAlphaPremultiplied(), null + ); + + return buffImage; + } + + static private ColorModel colorModel(int transparency) { + ColorModel model; + try { + model = java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDefaultConfiguration() + .getColorModel(transparency); + } + catch(HeadlessException he) { + model = ColorModel.getRGBdefault(); + } + return model; + } + + /** + * Key used for composite images -- it holds image identities + */ + private static class CompositeImageKey { + Image baseImage; + Image overlayImage; + int x; + int y; + + CompositeImageKey(Image base, Image overlay, int x, int y) { + this.x = x; + this.y = y; + this.baseImage = base; + this.overlayImage = overlay; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof CompositeImageKey)) { + return false; + } + + CompositeImageKey k = (CompositeImageKey) other; + + return (x == k.x) && (y == k.y) && (baseImage == k.baseImage) && (overlayImage == k.overlayImage); + } + + @Override + public int hashCode() { + int hash = ((x << 3) ^ y) << 4; + hash = hash ^ baseImage.hashCode() ^ overlayImage.hashCode(); + + return hash; + } + + @Override + public String toString() { + return "Composite key for " + baseImage + " + " + overlayImage + " at [" + x + ", " + y + "]"; // NOI18N + } + } + + /** + * Key used for ToolTippedImage + */ + private static class ToolTipImageKey { + Image image; + String str; + + ToolTipImageKey(Image image, String str) { + this.image = image; + this.str = str; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof ToolTipImageKey)) { + return false; + } + ToolTipImageKey k = (ToolTipImageKey) other; + return (str.equals(k.str)) && (image == k.image); + } + + @Override + public int hashCode() { + int hash = image.hashCode() ^ str.hashCode(); + return hash; + } + + @Override + public String toString() { + return "ImageStringKey for " + image + " + " + str; // NOI18N + } + } + + /** Cleaning reference. */ + private static final class ActiveRef extends SoftReference implements Runnable { + private Map> holder; + private T key; + + public ActiveRef(Image o, Map> holder, T key) { + super(o, Utilities.activeReferenceQueue()); + this.holder = holder; + this.key = key; + } + + public void run() { + synchronized (holder) { + holder.remove(key); + } + } + } + // end of ActiveRef + + /** + * Image with tool tip text (for icons with badges) + */ + private static class ToolTipImage extends BufferedImage implements Icon { + final String toolTipText; + + public static ToolTipImage createNew(String toolTipText, Image image) { + ImageUtilities.ensureLoaded(image); + boolean bitmask = (image instanceof Transparency) && ((Transparency) image).getTransparency() != Transparency.TRANSLUCENT; + ColorModel model = colorModel(bitmask ? Transparency.BITMASK : Transparency.TRANSLUCENT); + int w = image.getWidth(null); + int h = image.getHeight(null); + ToolTipImage newImage = new ToolTipImage(toolTipText, model, model.createCompatibleWritableRaster(w, h), model.isAlphaPremultiplied(), null); + + java.awt.Graphics g = newImage.createGraphics(); + g.drawImage(image, 0, 0, null); + g.dispose(); + return newImage; + } + + public ToolTipImage(String toolTipText, ColorModel cm, WritableRaster raster, boolean isRasterPremultiplied, Hashtable properties) { + super(cm, raster, isRasterPremultiplied, properties); + this.toolTipText = toolTipText; + } + + public ToolTipImage(String toolTipText, int width, int height, int imageType, IndexColorModel cm) { + super(width, height, imageType, cm); + this.toolTipText = toolTipText; + } + + public ToolTipImage(String toolTipText, int width, int height, int imageType) { + super(width, height, imageType); + this.toolTipText = toolTipText; + } + + public ToolTipImage(String toolTipText, BufferedImage image) { + super(image.getWidth(), image.getHeight(), image.getType()); + this.toolTipText = toolTipText; + } + + + public int getIconHeight() { + return super.getHeight(); + } + + public int getIconWidth() { + return super.getWidth(); + } + + public void paintIcon(Component c, Graphics g, int x, int y) { + g.drawImage(this, x, y, null); + } + } +} diff -r 055826baee46 openide.util/src/org/openide/util/Utilities.java --- a/openide.util/src/org/openide/util/Utilities.java Fri Jun 13 10:15:48 2008 +0200 +++ b/openide.util/src/org/openide/util/Utilities.java Fri Jun 13 12:59:38 2008 +0200 @@ -48,7 +48,6 @@ import java.awt.Dialog; import java.awt.Dimension; import java.awt.Frame; -import java.awt.Graphics; import java.awt.GraphicsConfiguration; import java.awt.GraphicsEnvironment; import java.awt.Image; @@ -61,7 +60,6 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; -import java.awt.image.BufferedImage; import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -99,8 +97,6 @@ import java.util.logging.Logger; import javax.swing.Action; import javax.swing.Icon; -import javax.swing.ImageIcon; -import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JSeparator; @@ -2588,33 +2584,28 @@ * over the first one with its top-left corner at x, y. Images need not be of the same size. * New image will have a size of max(second image size + top-left corner, first image size). * Method is used mostly when second image contains transparent pixels (e.g. for badging). - * If both images are null, it makes default transparent 16x16 image. * @param image1 underlying image * @param image2 second image * @param x x position of top-left corner * @param y y position of top-left corner * @return new merged image + * @deprecated Use ImageUtilities.mergeImages(). */ + @Deprecated public static final Image mergeImages(Image image1, Image image2, int x, int y) { - if (image1 == null) { - throw new NullPointerException(); - } - - if (image2 == null) { - throw new NullPointerException(); - } - - return IconManager.mergeImages(image1, image2, x, y); + return ImageUtilities.mergeImages(image1, image2, x, y); } - + /** * Loads an image from the specified resource ID. The image is loaded using the "system" classloader registered in * Lookup. * @param resourceID resource path of the icon (no initial slash) * @return icon's Image, or null, if the icon cannot be loaded. + * @deprecated Use ImageUtilities.loadImage(). */ + @Deprecated public static final Image loadImage(String resourceID) { - return IconManager.getIcon(resourceID, false); + return ImageUtilities.loadImage(resourceID); } /** @@ -2622,17 +2613,11 @@ * * @param icon {@link javax.swing.Icon} to be converted. * @since 7.3 + * @deprecated Use ImageUtilities.iconToImage(). */ + @Deprecated public static final Image icon2Image(Icon icon) { - if (icon instanceof ImageIcon) { - return ((ImageIcon) icon).getImage(); - } else { - BufferedImage bImage = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB); - Graphics g = bImage.getGraphics(); - icon.paintIcon(new JLabel(), g, 0, 0); - g.dispose(); - return bImage; - } + return ImageUtilities.icon2Image(icon); } /** Builds a popup menu from actions for provided context specified by @@ -2796,9 +2781,11 @@ *

Caching of loaded images can be used internally to improve performance. * * @since 3.24 + * @deprecated Use ImageUtilities.loadImage(). */ + @Deprecated public static final Image loadImage(String resource, boolean localized) { - return IconManager.getIcon(resource, localized); + return ImageUtilities.loadImage(resource, localized); } /** @@ -2865,7 +2852,7 @@ } // need to resize the icon - Image empty = IconManager.createBufferedImage(d.width, d.height); + Image empty = ImageUtilities.createBufferedImage(d.width, d.height); i = Utilities.mergeImages(icon, empty, 0, 0); } @@ -3038,14 +3025,17 @@ this.deprecated = deprecated; } + @Override public Reference poll() { throw new UnsupportedOperationException(); } + @Override public Reference remove(long timeout) throws IllegalArgumentException, InterruptedException { throw new InterruptedException(); } + @Override public Reference remove() throws InterruptedException { throw new InterruptedException(); } diff -r 055826baee46 openide.util/test/unit/src/org/openide/util/IconManagerGetLoaderTest.java --- a/openide.util/test/unit/src/org/openide/util/IconManagerGetLoaderTest.java Fri Jun 13 10:15:48 2008 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,148 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common - * Development and Distribution License("CDDL") (collectively, the - * "License"). You may not use this file except in compliance with the - * License. You can obtain a copy of the License at - * http://www.netbeans.org/cddl-gplv2.html - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the - * specific language governing permissions and limitations under the - * License. When distributing the software, include this License Header - * Notice in each file and include the License file at - * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the GPL Version 2 section of the License file that - * accompanied this code. If applicable, add the following below the - * License Header, with the fields enclosed by brackets [] replaced by - * your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * - * Contributor(s): - * - * The Original Software is NetBeans. The Initial Developer of the Original - * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun - * Microsystems, Inc. All Rights Reserved. - * - * If you wish your version of this file to be governed by only the CDDL - * or only the GPL Version 2, indicate your decision by adding - * "[Contributor] elects to include this software in this distribution - * under the [CDDL or GPL Version 2] license." If you do not indicate a - * single choice of license, a recipient has the option to distribute - * your version of this file under either the CDDL, the GPL Version 2 or - * to extend the choice of license to its licensees as provided above. - * However, if you add GPL Version 2 code and therefore, elected the GPL - * Version 2 license, then the option applies only if the new code is - * made subject to such option by the copyright holder. - */ -package org.openide.util; - -import java.net.URL; -import java.net.URLClassLoader; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.LogRecord; -import java.util.logging.Logger; -import junit.framework.*; -import java.lang.ref.*; -import java.util.*; - -/** - * - * @author Jaroslav Tulach - */ -public class IconManagerGetLoaderTest extends TestCase { - static { - System.setProperty("org.openide.util.Lookup", "org.openide.util.IconManagerGetLoaderTest$Lkp"); - - - Logger l = Logger.getLogger(""); - Handler[] arr = l.getHandlers(); - for (int i = 0; i < arr.length; i++) { - l.removeHandler(arr[i]); - } - l.addHandler(new ErrMgr()); - l.setLevel(Level.ALL); - } - - - public IconManagerGetLoaderTest (String testName) { - super (testName); - } - - protected void setUp () throws Exception { - } - - protected void tearDown () throws Exception { - } - - public static Test suite () { - TestSuite suite = new TestSuite(IconManagerGetLoaderTest.class); - return suite; - } - - - public void testWrongImplOfGetLoaderIssue62194() throws Exception { - ClassLoader l = IconManager.getLoader (); - assertTrue("Error manager race condition activated", ErrMgr.switchDone); - assertEquals("c1 the original one", Lkp.c1, l); - - ClassLoader n = IconManager.getLoader (); - assertEquals("c2 the new one", Lkp.c2, n); - } - - - - public static final class Lkp extends org.openide.util.lookup.AbstractLookup { - private org.openide.util.lookup.InstanceContent ic; - static ClassLoader c1 = new URLClassLoader(new URL[0]); - static ClassLoader c2 = new URLClassLoader(new URL[0]); - - public Lkp () { - this (new org.openide.util.lookup.InstanceContent ()); - } - - private Lkp (org.openide.util.lookup.InstanceContent ic) { - super (ic); - this.ic = ic; - - turn(c1); - } - - public void turn (ClassLoader c) { - ArrayList l = new ArrayList(); - l.add(c); - ic.set (l, null); - } - } - - - private static class ErrMgr extends Handler { - public static boolean switchDone; - - public void log (String s) { - if (s == null) return; - - if (s.startsWith ("Loader computed")) { - switchDone = true; - Lkp lkp = (Lkp)org.openide.util.Lookup.getDefault (); - lkp.turn (Lkp.c2); - } - } - - public void publish(LogRecord record) { - log(record.getMessage()); - } - - public void flush() { - } - - public void close() throws SecurityException { - } - - } - -} diff -r 055826baee46 openide.util/test/unit/src/org/openide/util/IconManagerTest.java --- a/openide.util/test/unit/src/org/openide/util/IconManagerTest.java Fri Jun 13 10:15:48 2008 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,116 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common - * Development and Distribution License("CDDL") (collectively, the - * "License"). You may not use this file except in compliance with the - * License. You can obtain a copy of the License at - * http://www.netbeans.org/cddl-gplv2.html - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the - * specific language governing permissions and limitations under the - * License. When distributing the software, include this License Header - * Notice in each file and include the License file at - * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the GPL Version 2 section of the License file that - * accompanied this code. If applicable, add the following below the - * License Header, with the fields enclosed by brackets [] replaced by - * your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * - * Contributor(s): - * - * The Original Software is NetBeans. The Initial Developer of the Original - * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun - * Microsystems, Inc. All Rights Reserved. - * - * If you wish your version of this file to be governed by only the CDDL - * or only the GPL Version 2, indicate your decision by adding - * "[Contributor] elects to include this software in this distribution - * under the [CDDL or GPL Version 2] license." If you do not indicate a - * single choice of license, a recipient has the option to distribute - * your version of this file under either the CDDL, the GPL Version 2 or - * to extend the choice of license to its licensees as provided above. - * However, if you add GPL Version 2 code and therefore, elected the GPL - * Version 2 license, then the option applies only if the new code is - * made subject to such option by the copyright holder. - */ -package org.openide.util; - -import java.awt.Color; -import java.awt.Image; -import java.awt.Transparency; -import java.awt.image.BufferedImage; -import junit.framework.*; -import java.lang.ref.*; -import java.util.*; - -/** - * - * @author Radim Kubacki - */ -public class IconManagerTest extends TestCase { - - public IconManagerTest (String testName) { - super (testName); - } - - public void testMergeImages() throws Exception { - // test if merged image preserves alpha (#90862) - BufferedImage img1 = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); -// System.out.println("img1 transparency "+img1.getTransparency()); - java.awt.Graphics2D g = img1.createGraphics(); - Color c = new Color(255, 255, 255, 128); - g.setColor(c); - g.fillRect(0, 0, 16, 16); - g.dispose(); - - BufferedImage img2 = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); -// System.out.println("img2 transparency "+img2.getTransparency()); - g = img2.createGraphics(); - c = new Color(255, 255, 255); - g.setColor(c); - g.fillRect(0, 0, 2, 2); - g.dispose(); - - Image mergedImg = IconManager.mergeImages(img1, img2, 0, 0); - if (!(mergedImg instanceof BufferedImage)) { - fail("It is assumed that mergeImages returns BufferedImage. Need to update test"); - } - - BufferedImage merged = (BufferedImage)mergedImg; -// System.out.println("pixels " + Integer.toHexString(merged.getRGB(10, 10)) +", "+ Integer.toHexString(merged.getRGB(0, 0))); - assertNotSame("transparency has to be kept for pixel <1,1>", merged.getRGB(10, 10), merged.getRGB(0, 0)); - } - - public void testMergeBitmaskImages() throws Exception { - // test if two bitmask images are merged to bitmask again to avoid use of alpha channel - BufferedImage img1 = new BufferedImage(16, 16, BufferedImage.TYPE_INT_RGB); -// System.out.println("img1 transparency "+img1.getTransparency()); - java.awt.Graphics2D g = img1.createGraphics(); - g.setColor(Color.BLUE); - g.fillRect(0, 0, 16, 16); - g.dispose(); - - BufferedImage img2 = new BufferedImage(2, 2, BufferedImage.TYPE_INT_RGB); -// System.out.println("img2 transparency "+img2.getTransparency()); - g = img2.createGraphics(); - g.setColor(Color.RED); - g.fillRect(0, 0, 2, 2); - g.dispose(); - - Image mergedImg = IconManager.mergeImages(img1, img2, 0, 0); - if (!(mergedImg instanceof BufferedImage)) { - fail("It is assumed that mergeImages returns BufferedImage. Need to update test"); - } - - BufferedImage merged = (BufferedImage)mergedImg; - assertEquals("Should create bitmask image", Transparency.BITMASK, merged.getTransparency()); - assertEquals(Color.RED, new Color(merged.getRGB(1, 1))); - assertEquals(Color.BLUE, new Color(merged.getRGB(10, 10))); - } - -} diff -r 055826baee46 openide.util/test/unit/src/org/openide/util/ImageUtilitiesGetLoaderTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/openide.util/test/unit/src/org/openide/util/ImageUtilitiesGetLoaderTest.java Fri Jun 13 12:59:38 2008 +0200 @@ -0,0 +1,150 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package org.openide.util; + +import java.net.URL; +import java.net.URLClassLoader; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import junit.framework.*; +import java.lang.ref.*; +import java.util.*; + +/** + * + * @author Jaroslav Tulach + */ +public class ImageUtilitiesGetLoaderTest extends TestCase { + static { + System.setProperty("org.openide.util.Lookup", "org.openide.util.ImageUtilitiesGetLoaderTest$Lkp"); + + + Logger l = Logger.getLogger(""); + Handler[] arr = l.getHandlers(); + for (int i = 0; i < arr.length; i++) { + l.removeHandler(arr[i]); + } + l.addHandler(new ErrMgr()); + l.setLevel(Level.ALL); + } + + + public ImageUtilitiesGetLoaderTest (String testName) { + super (testName); + } + + @Override + protected void setUp () throws Exception { + } + + @Override + protected void tearDown () throws Exception { + } + + public static Test suite () { + TestSuite suite = new TestSuite(ImageUtilitiesGetLoaderTest.class); + return suite; + } + + + public void testWrongImplOfGetLoaderIssue62194() throws Exception { + ClassLoader l = ImageUtilities.getLoader (); + assertTrue("Error manager race condition activated", ErrMgr.switchDone); + assertEquals("c1 the original one", Lkp.c1, l); + + ClassLoader n = ImageUtilities.getLoader (); + assertEquals("c2 the new one", Lkp.c2, n); + } + + + + public static final class Lkp extends org.openide.util.lookup.AbstractLookup { + private org.openide.util.lookup.InstanceContent ic; + static ClassLoader c1 = new URLClassLoader(new URL[0]); + static ClassLoader c2 = new URLClassLoader(new URL[0]); + + public Lkp () { + this (new org.openide.util.lookup.InstanceContent ()); + } + + private Lkp (org.openide.util.lookup.InstanceContent ic) { + super (ic); + this.ic = ic; + + turn(c1); + } + + public void turn (ClassLoader c) { + ArrayList l = new ArrayList(); + l.add(c); + ic.set (l, null); + } + } + + + private static class ErrMgr extends Handler { + public static boolean switchDone; + + public void log (String s) { + if (s == null) return; + + if (s.startsWith ("Loader computed")) { + switchDone = true; + Lkp lkp = (Lkp)org.openide.util.Lookup.getDefault (); + lkp.turn (Lkp.c2); + } + } + + public void publish(LogRecord record) { + log(record.getMessage()); + } + + public void flush() { + } + + public void close() throws SecurityException { + } + + } + +} diff -r 055826baee46 openide.util/test/unit/src/org/openide/util/ImageUtilitiesTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/openide.util/test/unit/src/org/openide/util/ImageUtilitiesTest.java Fri Jun 13 12:59:38 2008 +0200 @@ -0,0 +1,178 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package org.openide.util; + +import java.awt.Color; +import java.awt.Image; +import java.awt.Transparency; +import java.awt.image.BufferedImage; +import javax.swing.Icon; +import junit.framework.*; + +/** + * + * @author Radim Kubacki + */ +public class ImageUtilitiesTest extends TestCase { + + public ImageUtilitiesTest (String testName) { + super (testName); + } + + public void testMergeImages() throws Exception { + // test if merged image preserves alpha (#90862) + BufferedImage img1 = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); +// System.out.println("img1 transparency "+img1.getTransparency()); + java.awt.Graphics2D g = img1.createGraphics(); + Color c = new Color(255, 255, 255, 128); + g.setColor(c); + g.fillRect(0, 0, 16, 16); + g.dispose(); + + BufferedImage img2 = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); +// System.out.println("img2 transparency "+img2.getTransparency()); + g = img2.createGraphics(); + c = new Color(255, 255, 255); + g.setColor(c); + g.fillRect(0, 0, 2, 2); + g.dispose(); + + Image mergedImg = ImageUtilities.mergeImages(img1, img2, 0, 0); + if (!(mergedImg instanceof BufferedImage)) { + fail("It is assumed that mergeImages returns BufferedImage. Need to update test"); + } + + BufferedImage merged = (BufferedImage)mergedImg; +// System.out.println("pixels " + Integer.toHexString(merged.getRGB(10, 10)) +", "+ Integer.toHexString(merged.getRGB(0, 0))); + assertNotSame("transparency has to be kept for pixel <1,1>", merged.getRGB(10, 10), merged.getRGB(0, 0)); + } + + public void testMergeBitmaskImages() throws Exception { + // test if two bitmask images are merged to bitmask again to avoid use of alpha channel + BufferedImage img1 = new BufferedImage(16, 16, BufferedImage.TYPE_INT_RGB); +// System.out.println("img1 transparency "+img1.getTransparency()); + java.awt.Graphics2D g = img1.createGraphics(); + g.setColor(Color.BLUE); + g.fillRect(0, 0, 16, 16); + g.dispose(); + + BufferedImage img2 = new BufferedImage(2, 2, BufferedImage.TYPE_INT_RGB); +// System.out.println("img2 transparency "+img2.getTransparency()); + g = img2.createGraphics(); + g.setColor(Color.RED); + g.fillRect(0, 0, 2, 2); + g.dispose(); + + Image mergedImg = ImageUtilities.mergeImages(img1, img2, 0, 0); + if (!(mergedImg instanceof BufferedImage)) { + fail("It is assumed that mergeImages returns BufferedImage. Need to update test"); + } + + BufferedImage merged = (BufferedImage)mergedImg; + assertEquals("Should create bitmask image", Transparency.BITMASK, merged.getTransparency()); + assertEquals(Color.RED, new Color(merged.getRGB(1, 1))); + assertEquals(Color.BLUE, new Color(merged.getRGB(10, 10))); + } + + public void testImageToolTip() { + BufferedImage img1 = new BufferedImage(16, 16, BufferedImage.TYPE_INT_RGB); + java.awt.Graphics2D g = img1.createGraphics(); + g.setColor(Color.BLUE); + g.fillRect(0, 0, 16, 16); + g.dispose(); + + assertEquals("Tool tip text should be empty", "", ImageUtilities.getImageToolTip(img1)); + + String text = "test"; + Image imgTT1 = ImageUtilities.assignToolTipToImage(img1, text); + assertEquals("Should remain empty", "", ImageUtilities.getImageToolTip(img1)); + String str = ImageUtilities.getImageToolTip(imgTT1); + assertEquals("We should get what we set", text, str); + + Image imgTT2 = ImageUtilities.assignToolTipToImage(img1, "test"); + assertSame("Instances should be same", imgTT1, imgTT2); + + imgTT2 = ImageUtilities.addToolTipToImage(img1, ""); + imgTT2 = ImageUtilities.addToolTipToImage(imgTT2, "test"); + str = ImageUtilities.getImageToolTip(imgTT2); + String expected = "test"; + assertEquals("Tool tip text should be: " + expected + ", but it is " + str, expected, str); + + imgTT2 = ImageUtilities.addToolTipToImage(imgTT1, "test2"); + str = ImageUtilities.getImageToolTip(imgTT2); + expected = "test" + ImageUtilities.TOOLTIP_SEPAR + "test2"; + assertEquals("Tool tip text should be: " + expected + ", but it is " + str, expected, str); + + BufferedImage img2 = new BufferedImage(2, 2, BufferedImage.TYPE_INT_RGB); + g = img2.createGraphics(); + g.setColor(Color.RED); + g.fillRect(0, 0, 2, 2); + g.dispose(); + + imgTT1 = ImageUtilities.assignToolTipToImage(img1, "Tool tip image1"); + imgTT2 = ImageUtilities.assignToolTipToImage(img2, "Tool tip image2"); + Image result = ImageUtilities.mergeImages(imgTT1, imgTT2, 0, 0); + expected = "Tool tip image1" + ImageUtilities.TOOLTIP_SEPAR + "Tool tip image2"; + str = ImageUtilities.getImageToolTip(result); + assertEquals("Tool tip text should be: " + expected + ", but it is " + str, expected, str); + + result = ImageUtilities.mergeImages(imgTT1, img2, 0, 0); + str = ImageUtilities.getImageToolTip(result); + expected = "Tool tip image1"; + assertEquals("Tool tip text should be: " + expected + ", but it is " + str, expected, str); + + result = ImageUtilities.mergeImages(img1, imgTT2, 0, 0); + str = ImageUtilities.getImageToolTip(result); + expected = "Tool tip image2"; + assertEquals("Tool tip text should be: " + expected + ", but it is " + str, expected, str); + + result = ImageUtilities.mergeImages(img1, img2, 0, 0); + str = ImageUtilities.getImageToolTip(result); + expected = ""; + assertEquals("Tool tip text should be empty, but it is " + str, expected, str); + + Icon icon = ImageUtilities.image2Icon(result); + assertSame("Should be same instance", icon, result); + + Image img = ImageUtilities.icon2Image(icon); + assertSame("Should be same instance", icon, img); + } +}