// BlogBridge -- RSS feed reader, manager, and web based service // Copyright (C) 2002-2006 by R. Pito Salas // // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free Software Foundation; // either version 2 of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along with this program; // if not, write to the Free Software Foundation, Inc., 59 Temple Place, // Suite 330, Boston, MA 02111-1307 USA // // Contact: R. Pito Salas // mailto:pitosalas@users.sourceforge.net // More information: about BlogBridge // http://www.blogbridge.com // http://sourceforge.net/projects/blogbridge // // $Id: OSXSupport.java,v 1.21 2007/10/01 17:03:27 spyromus Exp $ // package com.salas.bb.utils.osx; import com.jgoodies.uif.osx.OSXApplicationMenu; import com.jgoodies.uif.util.ResourceUtils; import com.jgoodies.uif.util.SystemUtils; import com.salas.bb.utils.apple.AppleApplication; import com.salas.bb.utils.apple.IAppleApplicationListener; import com.salas.bb.utils.i18n.Strings; import com.salas.bb.utils.ipc.IPC; import com.salas.bb.utils.uif.laf.MacLookAndFeel; import javax.swing.*; import java.awt.*; import java.awt.event.ActionListener; import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.security.AllPermission; import java.security.CodeSource; import java.security.PermissionCollection; import java.security.ProtectionDomain; import java.text.MessageFormat; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; /** * Interface layer to Mac OSX. Contains both local functionality and interfaces to JGoodies OSX * Support. Utility. */ public final class OSXSupport { private static final Logger LOG = Logger.getLogger(OSXSupport.class.getName()); /** * Width of splitter pane. */ public static final int SPLITPANE_WIDTH = 8; private static ClassLoader cocoaClassLoader; /** * Hidden constructor of utility class. */ private OSXSupport() { } /** * Set the name of the Application Menu. * * @param aString - Application menu */ public static void setAboutMenuName(String aString) { if (SystemUtils.IS_OS_MAC) OSXApplicationMenu.setAboutName(aString); } /** * Tell OSX that you want the main window to be Metal. N.B. It is crucial that this method is * called before any Swing related code is run. This includes even 'new'ing a class derived from * JFrame. If it looks like this method isn't doing anything, it's probably because it has been * called too late. */ public static void setMetalOSXLook() { System.setProperty("apple.awt.brushMetalLook", "true"); } /** * Declare the ActionListeners to be invoked when the indicated commands picked off the * Application menu of OSX. Note: noop if we are not on a mac. * * @param about About action * @param prefs Show Preferences action * @param exit Quit Application action */ public static void setApplicationMenu(ActionListener about, ActionListener prefs, ActionListener exit) { if (SystemUtils.IS_OS_MAC) OSXApplicationMenu.register(about, prefs, exit); } /** * Initializes Mac-specific LAF. */ public static void setupLAF() { if (!SystemUtils.IS_OS_MAC) return; try { UIManager.setLookAndFeel(new MacLookAndFeel()); } catch (Exception e) { LOG.log(Level.SEVERE, Strings.error("unhandled.exception"), e); } } /** * Returns Cocoa class by name. * * @param name name of the class (i.e. <code>com.apple.cocoa.application.NSImage</code>). * * @return class. * * @throws ClassNotFoundException if class not found. */ public static Class getCocoaClass(String name) throws ClassNotFoundException { if (!SystemUtils.IS_OS_MAC) return null; return getCocoaClassLoader().loadClass(name); } /** * Returns classloader for Cocoa classes. * * @return class loader. */ public static synchronized ClassLoader getCocoaClassLoader() { if (!SystemUtils.IS_OS_MAC) return null; if (cocoaClassLoader == null) { // Setup Cocoa Class Loader plus Growl delegate which should be on the // same class loader with the rest of Cocoa tools ClassLoader currentCL = OSXSupport.class.getClassLoader(); URL urlCocoa = null; try { urlCocoa = new URL("file:///System/Library/Java/"); } catch (MalformedURLException e) { // Impossible } ClassLoader cl = new AllPermissionsURLClassLoader(new URL[] { urlCocoa }, currentCL); cocoaClassLoader = new ResourceClassLoader("resources/growl", cl); } return cocoaClassLoader; } /** * Loads resource into the array of bytes. * * @param resource resource to load. * * @return array of bytes or <code>NULL</code> if not found. */ public static byte[] loadResourceBytes(String resource) { byte[] buffer = null; InputStream stream = OSXSupport.class.getClassLoader().getResourceAsStream(resource); if (stream != null) { BufferedInputStream bis = new BufferedInputStream(stream); try { buffer = new byte[bis.available()]; bis.read(buffer); } catch (IOException e) { buffer = null; LOG.log(Level.SEVERE, Strings.error("notify.failed.to.load.resource"), e); } finally { try { bis.close(); } catch (IOException e) { } } } else LOG.log(Level.SEVERE, MessageFormat.format(Strings.error("resource.not.found"), new Object[] { resource })); return buffer; } /** * Sets the application icon. */ public static void setApplicationIcon() { if (!SystemUtils.IS_OS_MAC) return; Image image = ResourceUtils.readImage("blogbridge.icns"); DockIcon.setApplicationIcon(image); } /** * Configures Mac-specific IPC. * * @param ipc IPC module. */ public static void setupIPC(final IPC ipc) { AppleApplication.addApplicationListener(new IAppleApplicationListener() { /** * Invoked when Finder gives the command to open some file. * * @param filename file name. * * @return <code>TRUE</code> if the action was performed. */ public boolean handleOpenFile(String filename) { if (filename == null) return false; URL u = null; // Check if it's URL try { u = new URL(filename); } catch (MalformedURLException e) { // Not URL } // Check if it's filename if (u == null) { File f = new File(filename); try { u = f.toURL(); } catch (MalformedURLException e) { // Not filename } } if (u != null) ipc.fireSubscribe(u); return u == null; } }); } /** * The class loader which is taking classes from the resources. */ private static class ResourceClassLoader extends ClassLoader { private final Map classes = new HashMap(); private final String root; /** * Creates a new class loader. * * @param root offset from the resources root to start loading classes from. * @param parent parent class loader. */ public ResourceClassLoader(String root, ClassLoader parent) { super(parent); this.root = root; } /** * Loads the class with the specified name. This method searches for * classes in the same manner as the {@link #loadClass(String, boolean)} * method. It is invoked by the Java virtual machine to resolveURI class * references. Invoking this method is equivalent to invoking {@link * #loadClass(String, boolean) <tt>loadClass(name, false)</tt>}. </p> * * @param name The name of the class * @return The resulting <tt>Class</tt> object * @throws ClassNotFoundException If the class was not found */ public Class loadClass(String name) throws ClassNotFoundException { return loadClass(name, true); } /** * Loads the class with the specified name. The default implementation * of this method searches for classes in the following order: * <p/> * <p><ol> * <p/> * <li><p> Invoke {@link #findLoadedClass(String)} to check if the class * has already been loaded. </p></li> * <p/> * <li><p> Invoke the {@link #loadClass(String) <tt>loadClass</tt>} method * on the parent class loader. If the parent is <tt>null</tt> the class * loader built-in to the virtual machine is used, instead. </p></li> * <p/> * <li><p> Invoke the {@link #findClass(String)} method to find the * class. </p></li> * <p/> * </ol> * <p/> * <p> If the class was found using the above steps, and the * <tt>resolveURI</tt> flag is true, this method will then invoke the {@link * #resolveClass(Class)} method on the resulting <tt>Class</tt> object. * <p/> * <p> Subclasses of <tt>ClassLoader</tt> are encouraged to override {@link * #findClass(String)}, rather than this method. </p> * * @param name The name of the class * @param resolve If <tt>true</tt> then resolveURI the class * @return The resulting <tt>Class</tt> object * @throws ClassNotFoundException If the class could not be found */ protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { Class result; result = (Class)classes.get(name); if (result == null) { try { result = super.loadClass(name, resolve); } catch (ClassNotFoundException e) { // Not a parent class-loader class } if (result == null) { byte[] classBytes = loadClassBytes(name); if (classBytes != null) { ProtectionDomain protectionDomain = ResourceClassLoader.class.getProtectionDomain(); result = defineClass(name, classBytes, 0, classBytes.length, protectionDomain); if (result == null) throw new ClassFormatError(name); // Put class in cache classes.put(name, result); } } } return result; } /** * Loads class bytes. * * @param name name of the class. * * @return bytes. * * @throws ClassNotFoundException if class couldn't be found. */ private byte[] loadClassBytes(String name) throws ClassNotFoundException { String resource = root + "/" + name.replace('.', '/') + ".class"; byte[] buffer = loadResourceBytes(resource); if (buffer == null || buffer.length == 0) throw new ClassNotFoundException(name); return buffer; } } /** * URL class loader setting all permissions to the classes it loads. */ private static class AllPermissionsURLClassLoader extends URLClassLoader { private PermissionCollection pc; /** * Creates loader. * * @param urls the list of URLs to treat as roots. * @param parent parent class loader. */ public AllPermissionsURLClassLoader(URL[] urls, ClassLoader parent) { super(urls, parent); AllPermission ap = new AllPermission(); pc = ap.newPermissionCollection(); pc.add(ap); } /** * Returns the permissions for the code source. * * @param codesource code source. * * @return permissions. */ protected PermissionCollection getPermissions(CodeSource codesource) { return pc; } } }