ch.cyberduck.core.local.LaunchServicesApplicationFinder.java Source code

Java tutorial

Introduction

Here is the source code for ch.cyberduck.core.local.LaunchServicesApplicationFinder.java

Source

package ch.cyberduck.core.local;

/*
 * Copyright (c) 2012 David Kocher. All rights reserved.
 * http://cyberduck.ch/
 *
 * 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.
 *
 * Bug fixes, suggestions and comments should be sent to:
 * dkocher@cyberduck.ch
 */

import ch.cyberduck.binding.application.NSWorkspace;
import ch.cyberduck.binding.foundation.NSBundle;
import ch.cyberduck.binding.foundation.NSDictionary;
import ch.cyberduck.binding.foundation.NSObject;
import ch.cyberduck.core.Local;
import ch.cyberduck.core.LocalFactory;
import ch.cyberduck.core.library.Native;

import org.apache.commons.collections4.map.LRUMap;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

public final class LaunchServicesApplicationFinder implements ApplicationFinder {
    private static final Logger log = Logger.getLogger(LaunchServicesApplicationFinder.class);

    static {
        Native.load("core");
    }

    public LaunchServicesApplicationFinder() {
        //
    }

    /**
     * Uses LSGetApplicationForInfo
     *
     * @param extension File extension
     * @return Null if not found
     */
    private native String findForType(String extension);

    /**
     * Uses LSCopyAllRoleHandlersForContentType
     *
     * @param extension File extension
     * @return Empty array if none found
     */
    private native String[] findAllForType(String extension);

    /**
     * Uses LSRegisterURL
     *
     * @param path Location of application bundle
     */
    private native boolean register(String path);

    /**
     * Caching map between application bundle identifier and
     * display name of application
     */
    @SuppressWarnings("unchecked")
    private static final Map<String, Application> applicationNameCache = Collections
            .synchronizedMap(new LRUMap<String, Application>(20));

    /**
     *
     */
    @SuppressWarnings("unchecked")
    private static final Map<String, Application> defaultApplicationCache = Collections
            .synchronizedMap(new LRUMap<String, Application>(20));

    /**
     * Caching map between application bundle identifiers and
     * file type extensions.
     */
    @SuppressWarnings("unchecked")
    private static final Map<String, List<Application>> defaultApplicationListCache = Collections
            .synchronizedMap(new LRUMap<String, List<Application>>(20));

    @Override
    public List<Application> findAll(final String filename) {
        final String extension = FilenameUtils.getExtension(filename);
        if (StringUtils.isEmpty(extension)) {
            return Collections.emptyList();
        }
        if (!defaultApplicationListCache.containsKey(extension)) {
            final List<Application> applications = new ArrayList<Application>();
            for (String identifier : this.findAllForType(extension)) {
                applications.add(this.getDescription(identifier));
            }
            // Because of the different API used the default opening application may not be included
            // in the above list returned. Always add the default application anyway.
            final Application defaultApplication = this.find(filename);
            if (this.isInstalled(defaultApplication)) {
                if (!applications.contains(defaultApplication)) {
                    applications.add(defaultApplication);
                }
            }
            defaultApplicationListCache.put(extension, applications);
        }
        return defaultApplicationListCache.get(extension);
    }

    /**
     * The default application for this file as set by the launch services
     *
     * @param filename Filename
     * @return The bundle identifier of the default application to open the
     * file of this type or null if unknown
     */
    @Override
    public Application find(final String filename) {
        final String extension = FilenameUtils.getExtension(filename);
        if (!defaultApplicationCache.containsKey(extension)) {
            if (StringUtils.isEmpty(extension)) {
                return Application.notfound;
            }
            final String path = this.findForType(extension);
            if (StringUtils.isEmpty(path)) {
                defaultApplicationCache.put(extension, Application.notfound);
            } else {
                final NSBundle bundle = NSBundle.bundleWithPath(path);
                if (null == bundle) {
                    log.error(String.format("Loading bundle %s failed", path));
                    defaultApplicationCache.put(extension, Application.notfound);
                } else {
                    defaultApplicationCache.put(extension, this.getDescription(bundle.bundleIdentifier()));
                }
            }
        }
        return defaultApplicationCache.get(extension);
    }

    /**
     * Determine the human readable application name for a given bundle identifier.
     *
     * @param search Bundle identifier
     * @return Application human readable name
     */
    @Override
    public Application getDescription(final String search) {
        if (applicationNameCache.containsKey(search)) {
            return applicationNameCache.get(search);
        }
        if (log.isDebugEnabled()) {
            log.debug(String.format("Find application for %s", search));
        }
        final String identifier;
        final String name;
        synchronized (NSWorkspace.class) {
            final NSWorkspace workspace = NSWorkspace.sharedWorkspace();
            final String path;
            if (null != workspace.absolutePathForAppBundleWithIdentifier(search)) {
                path = workspace.absolutePathForAppBundleWithIdentifier(search);
            } else {
                log.warn(String.format(
                        "Cannot determine installation path for bundle identifier %s. Try with name.", search));
                path = workspace.fullPathForApplication(search);
            }
            if (StringUtils.isNotBlank(path)) {
                final NSBundle app = NSBundle.bundleWithPath(path);
                if (null == app) {
                    log.error(String.format("Loading bundle %s failed", path));
                    identifier = search;
                    name = FilenameUtils.removeExtension(LocalFactory.get(path).getDisplayName());
                } else {
                    NSDictionary dict = app.infoDictionary();
                    if (null == dict) {
                        log.error(String.format("Loading application dictionary for bundle %s failed", path));
                        applicationNameCache.put(search, Application.notfound);
                        return null;
                    } else {
                        final NSObject bundlename = dict.objectForKey("CFBundleName");
                        if (null == bundlename) {
                            log.warn(String.format("No CFBundleName in bundle %s", path));
                            name = FilenameUtils.removeExtension(LocalFactory.get(path).getDisplayName());
                        } else {
                            name = bundlename.toString();
                        }
                        final NSObject bundleIdentifier = dict.objectForKey("CFBundleIdentifier");
                        if (null == bundleIdentifier) {
                            log.warn(String.format("No CFBundleName in bundle %s", path));
                            identifier = search;
                        } else {
                            identifier = bundleIdentifier.toString();
                        }

                    }
                }
            } else {
                log.warn(String.format("Cannot determine installation path for %s", search));
                applicationNameCache.put(search, Application.notfound);
                return Application.notfound;
            }
        }
        final Application application = new Application(identifier, name);
        applicationNameCache.put(search, application);
        return application;
    }

    @Override
    public boolean isInstalled(final Application application) {
        synchronized (NSWorkspace.class) {
            if (Application.notfound.equals(application)) {
                return false;
            }
            return NSWorkspace.sharedWorkspace()
                    .absolutePathForAppBundleWithIdentifier(application.getIdentifier()) != null;
        }
    }

    /**
     * Register application in launch services database
     *
     * @param application Bundle identifier
     */
    public boolean register(final Local application) {
        synchronized (NSWorkspace.class) {
            if (!application.exists()) {
                return false;
            }
            return this.register(application.getAbsolute());
        }
    }
}