org.eclipse.pde.nls.internal.ui.model.ResourceBundleModel.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.pde.nls.internal.ui.model.ResourceBundleModel.java

Source

/*******************************************************************************
 * Copyright (c) 2008 Stefan Mcke 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:
 *     Stefan Mcke - initial API and implementation
 *******************************************************************************/
package org.eclipse.pde.nls.internal.ui.model;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Locale;

import org.eclipse.babel.editor.plugin.MessagesEditorPlugin;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJarEntryResource;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.osgi.service.resolver.BundleDescription;

/**
 * A <code>ResourceBundleModel</code> is the host for all
 * {@link ResourceBundleFamily} elements.
 */
public class ResourceBundleModel extends ResourceBundleElement {

    private static final String PROPERTIES_SUFFIX = ".properties"; //$NON-NLS-1$

    private static final String JAVA_NATURE = "org.eclipse.jdt.core.javanature"; //$NON-NLS-1$

    private ArrayList<ResourceBundleFamily> bundleFamilies = new ArrayList<ResourceBundleFamily>();

    /**
     * The locales for which all bundles have been loaded.
     */
    // TODO Perhaps we should add reference counting to prevent unexpected
    // unloading of bundles
    private HashSet<Locale> loadedLocales = new HashSet<Locale>();

    public ResourceBundleModel(IProgressMonitor monitor) {
        super(null);
        try {
            populateFromWorkspace(monitor);
        } catch (CoreException e) {
            MessagesEditorPlugin.log(e);
        }
    }

    /**
     * Returns all resource bundle families contained in this model.
     * 
     * @return all resource bundle families contained in this model
     */
    public ResourceBundleFamily[] getFamilies() {
        return bundleFamilies.toArray(new ResourceBundleFamily[bundleFamilies.size()]);
    }

    public ResourceBundleFamily[] getFamiliesForPluginId(String pluginId) {
        ArrayList<ResourceBundleFamily> found = new ArrayList<ResourceBundleFamily>();
        for (ResourceBundleFamily family : bundleFamilies) {
            if (family.getPluginId().equals(pluginId)) {
                found.add(family);
            }
        }
        return found.toArray(new ResourceBundleFamily[found.size()]);
    }

    public ResourceBundleFamily[] getFamiliesForProjectName(String projectName) {
        ArrayList<ResourceBundleFamily> found = new ArrayList<ResourceBundleFamily>();
        for (ResourceBundleFamily family : bundleFamilies) {
            if (family.getProjectName().equals(projectName)) {
                found.add(family);
            }
        }
        return found.toArray(new ResourceBundleFamily[found.size()]);
    }

    public ResourceBundleFamily[] getFamiliesForProject(IProject project) {
        return getFamiliesForProjectName(project.getName());
    }

    /**
     * Returns an array of all currently known bundle keys. This always includes
     * the keys from the default bundles and may include some additional keys
     * from bundles that have been loaded sometime and that contain keys not
     * found in a bundle's default bundle. When a bundle is unloaded, these
     * additional keys will not be removed from the model.
     * 
     * @return the array of bundles keys
     * @throws CoreException
     */
    public ResourceBundleKey[] getAllKeys() throws CoreException {
        Locale root = new Locale("", "", "");

        // Ensure default bundle is loaded and count keys
        int size = 0;
        for (ResourceBundleFamily family : bundleFamilies) {
            ResourceBundle bundle = family.getBundle(root);
            if (bundle != null)
                bundle.load();
            size += family.getKeyCount();
        }

        ArrayList<ResourceBundleKey> allKeys = new ArrayList<ResourceBundleKey>(size);
        for (ResourceBundleFamily family : bundleFamilies) {
            ResourceBundleKey[] keys = family.getKeys();
            for (ResourceBundleKey key : keys) {
                allKeys.add(key);
            }
        }

        return allKeys.toArray(new ResourceBundleKey[allKeys.size()]);
    }

    /**
     * Loads all the bundles for the given locale into memory.
     * 
     * @param locale
     *            the locale of the bundles to load
     * @throws CoreException
     */
    public void loadBundles(Locale locale) throws CoreException {
        ResourceBundleFamily[] families = getFamilies();
        for (ResourceBundleFamily family : families) {
            ResourceBundle bundle = family.getBundle(locale);
            if (bundle != null)
                bundle.load();
        }
        loadedLocales.add(locale);
    }

    /**
     * Unloads all the bundles for the given locale from this model. The default
     * bundle cannot be unloaded. Such a request will be ignored.
     * 
     * @param locale
     *            the locale of the bundles to unload
     */
    public void unloadBundles(Locale locale) {
        if ("".equals(locale.getLanguage()))
            return; // never unload the default bundles

        ResourceBundleFamily[] families = getFamilies();
        for (ResourceBundleFamily family : families) {
            ResourceBundle bundle = family.getBundle(locale);
            if (bundle != null)
                bundle.unload();
        }
        loadedLocales.remove(locale);
    }

    /**
     * @param monitor
     * @throws CoreException
     */
    private void populateFromWorkspace(IProgressMonitor monitor) throws CoreException {
        IWorkspace workspace = ResourcesPlugin.getWorkspace();
        IWorkspaceRoot root = workspace.getRoot();
        IProject[] projects = root.getProjects();
        for (IProject project : projects) {
            try {
                if (!project.isOpen())
                    continue;

                IJavaProject javaProject = (IJavaProject) project.getNature(JAVA_NATURE);
                String pluginId = null;

                try {
                    Class IFragmentModel = Class.forName("org.eclipse.pde.core.plugin.IFragmentModel");
                    Class IPluginModelBase = Class.forName("org.eclipse.pde.core.plugin.IPluginModelBase");
                    Class PluginRegistry = Class.forName("org.eclipse.pde.core.plugin.PluginRegistry");
                    Class IPluginBase = Class.forName("org.eclipse.pde.core.plugin.IPluginBase");
                    Class PluginFragmentModel = Class.forName("org.eclipse.core.runtime.model.PluginFragmentModel");

                    // Plugin and fragment projects
                    Class pluginModel = (Class) PluginRegistry.getMethod("findModel", IProject.class).invoke(null,
                            project);
                    if (pluginModel != null) {
                        // Get plugin id
                        BundleDescription bd = (BundleDescription) IPluginModelBase
                                .getMethod("getBundleDescription").invoke(pluginModel);
                        pluginId = bd.getName();
                        // OSGi bundle name
                        if (pluginId == null) {
                            Object pluginBase = IPluginModelBase.getMethod("getPluginBase").invoke(pluginModel);
                            pluginId = (String) IPluginBase.getMethod("getId").invoke(pluginBase); // non-OSGi
                            // plug-in id
                        }

                        boolean isFragment = IFragmentModel.isInstance(pluginModel);
                        if (isFragment) {
                            Object pfm = IFragmentModel.getMethod("getFragment");
                            pluginId = (String) PluginFragmentModel.getMethod("getPluginId").invoke(pfm);
                        }

                        // Look for additional 'nl' resources
                        IFolder nl = project.getFolder("nl"); //$NON-NLS-1$
                        if (isFragment && nl.exists()) {
                            IResource[] members = nl.members();
                            for (IResource member : members) {
                                if (member instanceof IFolder) {
                                    IFolder langFolder = (IFolder) member;
                                    String language = langFolder.getName();

                                    // Collect property files
                                    IFile[] propertyFiles = collectPropertyFiles(langFolder);
                                    for (IFile file : propertyFiles) {
                                        // Compute path name
                                        IPath path = file.getProjectRelativePath();
                                        String country = ""; //$NON-NLS-1$
                                        String packageName = null;
                                        int segmentCount = path.segmentCount();
                                        if (segmentCount > 1) {
                                            StringBuilder builder = new StringBuilder();

                                            // Segment 0: 'nl'
                                            // Segment 1: language code
                                            // Segment 2: (country code)
                                            int begin = 2;
                                            if (segmentCount > 2 && isCountry(path.segment(2))) {
                                                begin = 3;
                                                country = path.segment(2);
                                            }

                                            for (int i = begin; i < segmentCount - 1; i++) {
                                                if (i > begin)
                                                    builder.append('.');
                                                builder.append(path.segment(i));
                                            }
                                            packageName = builder.toString();
                                        }

                                        String baseName = getBaseName(file.getName());

                                        ResourceBundleFamily family = getOrCreateFamily(project.getName(), pluginId,
                                                packageName, baseName);
                                        addBundle(family, getLocale(language, country), file);
                                    }
                                }
                            }
                        }

                        // Collect property files
                        if (isFragment || javaProject == null) {
                            IFile[] propertyFiles = collectPropertyFiles(project);
                            for (IFile file : propertyFiles) {
                                IPath path = file.getProjectRelativePath();
                                int segmentCount = path.segmentCount();

                                if (segmentCount > 0 && path.segment(0).equals("nl")) //$NON-NLS-1$
                                    continue; // 'nl' resource have been
                                // processed
                                // above

                                // Guess package name
                                String packageName = null;
                                if (segmentCount > 1) {
                                    StringBuilder builder = new StringBuilder();
                                    for (int i = 0; i < segmentCount - 1; i++) {
                                        if (i > 0)
                                            builder.append('.');
                                        builder.append(path.segment(i));
                                    }
                                    packageName = builder.toString();
                                }

                                String baseName = getBaseName(file.getName());
                                String language = getLanguage(file.getName());
                                String country = getCountry(file.getName());

                                ResourceBundleFamily family = getOrCreateFamily(project.getName(), pluginId,
                                        packageName, baseName);
                                addBundle(family, getLocale(language, country), file);
                            }
                        }

                    }
                } catch (Throwable e) {
                    // MessagesEditorPlugin.log(e);
                }

                // Look for resource bundles in Java packages (output folders,
                // e.g. 'bin', will be ignored)
                if (javaProject != null) {
                    IClasspathEntry[] classpathEntries = javaProject.getResolvedClasspath(true);
                    for (IClasspathEntry entry : classpathEntries) {
                        if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
                            IPath path = entry.getPath();
                            IFolder folder = workspace.getRoot().getFolder(path);
                            IFile[] propertyFiles = collectPropertyFiles(folder);

                            for (IFile file : propertyFiles) {
                                String name = file.getName();
                                String baseName = getBaseName(name);
                                String language = getLanguage(name);
                                String country = getCountry(name);
                                IPackageFragment pf = javaProject
                                        .findPackageFragment(file.getParent().getFullPath());
                                String packageName = pf.getElementName();

                                ResourceBundleFamily family = getOrCreateFamily(project.getName(), pluginId,
                                        packageName, baseName);

                                addBundle(family, getLocale(language, country), file);
                            }
                        } else if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
                            IPackageFragmentRoot[] findPackageFragmentRoots = javaProject
                                    .findPackageFragmentRoots(entry);
                            for (IPackageFragmentRoot packageFragmentRoot : findPackageFragmentRoots) {
                                IJavaElement[] children = packageFragmentRoot.getChildren();
                                for (IJavaElement child : children) {
                                    IPackageFragment pf = (IPackageFragment) child;
                                    Object[] nonJavaResources = pf.getNonJavaResources();

                                    for (Object resource : nonJavaResources) {
                                        if (resource instanceof IJarEntryResource) {
                                            IJarEntryResource jarEntryResource = (IJarEntryResource) resource;
                                            String name = jarEntryResource.getName();
                                            if (name.endsWith(PROPERTIES_SUFFIX)) {
                                                String baseName = getBaseName(name);
                                                String language = getLanguage(name);
                                                String country = getCountry(name);
                                                String packageName = pf.getElementName();

                                                ResourceBundleFamily family = getOrCreateFamily(project.getName(),
                                                        pluginId, packageName, baseName);

                                                addBundle(family, getLocale(language, country), jarEntryResource);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }

                    // Collect non-Java resources
                    Object[] nonJavaResources = javaProject.getNonJavaResources();
                    ArrayList<IFile> files = new ArrayList<IFile>();
                    for (Object resource : nonJavaResources) {
                        if (resource instanceof IContainer) {
                            IContainer container = (IContainer) resource;
                            collectPropertyFiles(container, files);
                        } else if (resource instanceof IFile) {
                            IFile file = (IFile) resource;
                            String name = file.getName();
                            if (isIgnoredFilename(name))
                                continue;
                            if (name.endsWith(PROPERTIES_SUFFIX)) {
                                files.add(file);
                            }
                        }
                    }
                    for (IFile file : files) {

                        // Convert path to package name format
                        IPath path = file.getProjectRelativePath();
                        String packageName = null;
                        int segmentCount = path.segmentCount();
                        if (segmentCount > 1) {
                            StringBuilder builder = new StringBuilder();
                            for (int i = 0; i < segmentCount - 1; i++) {
                                if (i > 0)
                                    builder.append('.');
                                builder.append(path.segment(i));
                            }
                            packageName = builder.toString();
                        }

                        String baseName = getBaseName(file.getName());
                        String language = getLanguage(file.getName());
                        String country = getCountry(file.getName());

                        ResourceBundleFamily family = getOrCreateFamily(project.getName(), pluginId, packageName,
                                baseName);
                        addBundle(family, getLocale(language, country), file);
                    }

                }
            } catch (Exception e) {
                MessagesEditorPlugin.log(e);
            }
        }
    }

    private IFile[] collectPropertyFiles(IContainer container) throws CoreException {
        ArrayList<IFile> files = new ArrayList<IFile>();
        collectPropertyFiles(container, files);
        return files.toArray(new IFile[files.size()]);
    }

    private void collectPropertyFiles(IContainer container, ArrayList<IFile> files) throws CoreException {
        IResource[] members = container.members();
        for (IResource resource : members) {
            if (!resource.exists())
                continue;
            if (resource instanceof IContainer) {
                IContainer childContainer = (IContainer) resource;
                collectPropertyFiles(childContainer, files);
            } else if (resource instanceof IFile) {
                IFile file = (IFile) resource;
                String name = file.getName();
                if (file.getProjectRelativePath().segmentCount() == 0 && isIgnoredFilename(name))
                    continue;
                if (name.endsWith(PROPERTIES_SUFFIX)) {
                    files.add(file);
                }
            }
        }
    }

    private boolean isCountry(String name) {
        if (name == null || name.length() != 2)
            return false;
        char c1 = name.charAt(0);
        char c2 = name.charAt(1);
        return 'A' <= c1 && c1 <= 'Z' && 'A' <= c2 && c2 <= 'Z';
    }

    private Locale getLocale(String language, String country) {
        if (language == null)
            language = ""; //$NON-NLS-1$
        if (country == null)
            country = ""; //$NON-NLS-1$
        return new Locale(language, country);
    }

    private void addBundle(ResourceBundleFamily family, Locale locale, Object resource) throws CoreException {
        ResourceBundle bundle = new ResourceBundle(family, resource, locale);
        if ("".equals(locale.getLanguage()))
            bundle.load();
        family.addBundle(bundle);
    }

    private String getBaseName(String filename) {
        if (!filename.endsWith(PROPERTIES_SUFFIX))
            throw new IllegalArgumentException();
        String name = filename.substring(0, filename.length() - 11);
        int len = name.length();
        if (len > 3 && name.charAt(len - 3) == '_') {
            if (len > 6 && name.charAt(len - 6) == '_') {
                return name.substring(0, len - 6);
            } else {
                return name.substring(0, len - 3);
            }
        }
        return name;
    }

    private String getLanguage(String filename) {
        if (!filename.endsWith(PROPERTIES_SUFFIX))
            throw new IllegalArgumentException();
        String name = filename.substring(0, filename.length() - 11);
        int len = name.length();
        if (len > 3 && name.charAt(len - 3) == '_') {
            if (len > 6 && name.charAt(len - 6) == '_') {
                return name.substring(len - 5, len - 3);
            } else {
                return name.substring(len - 2);
            }
        }
        return ""; //$NON-NLS-1$
    }

    private String getCountry(String filename) {
        if (!filename.endsWith(PROPERTIES_SUFFIX))
            throw new IllegalArgumentException();
        String name = filename.substring(0, filename.length() - 11);
        int len = name.length();
        if (len > 3 && name.charAt(len - 3) == '_') {
            if (len > 6 && name.charAt(len - 6) == '_') {
                return name.substring(len - 2);
            } else {
                return ""; //$NON-NLS-1$
            }
        }
        return ""; //$NON-NLS-1$
    }

    private ResourceBundleFamily getOrCreateFamily(String projectName, String pluginId, String packageName,
            String baseName) {

        // Ignore project name
        if (pluginId != null)
            projectName = null;

        for (ResourceBundleFamily family : bundleFamilies) {
            if (areEqual(family.getProjectName(), projectName) && areEqual(family.getPluginId(), pluginId)
                    && areEqual(family.getPackageName(), packageName) && areEqual(family.getBaseName(), baseName)) {
                return family;
            }
        }
        ResourceBundleFamily family = new ResourceBundleFamily(this, projectName, pluginId, packageName, baseName);
        bundleFamilies.add(family);
        return family;
    }

    private boolean isIgnoredFilename(String filename) {
        return filename.equals("build.properties") || filename.equals("logging.properties"); //$NON-NLS-1$ //$NON-NLS-2$
    }

    private boolean areEqual(String str1, String str2) {
        return str1 == null && str2 == null || str1 != null && str1.equals(str2);
    }

}