com.siteview.mde.internal.ui.nls.NLSFragmentGenerator.java Source code

Java tutorial

Introduction

Here is the source code for com.siteview.mde.internal.ui.nls.NLSFragmentGenerator.java

Source

/*******************************************************************************
 * Copyright (c) 2008, 2010 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package com.siteview.mde.internal.ui.nls;

import com.siteview.mde.internal.core.monitor.ExternalMonitorModelBase;

import com.siteview.mde.core.monitor.IMonitorModelBase;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.wizard.IWizardContainer;
import org.eclipse.osgi.util.NLS;
import com.siteview.mde.internal.core.ICoreConstants;
import com.siteview.mde.internal.core.TargetPlatformHelper;
import com.siteview.mde.internal.ui.MDEPlugin;
import com.siteview.mde.internal.ui.MDEUIMessages;
import com.siteview.mde.internal.ui.wizards.IProjectProvider;
import com.siteview.mde.internal.ui.wizards.plugin.FragmentFieldData;
import com.siteview.mde.internal.ui.wizards.plugin.NewProjectCreationOperation;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

/**
 * 
 * Generates the fragment projects for the list of selected plug-ins.
 * Each fragment contains a series of properties files specific to the locales
 * the user selected. For example plugin_fr.properties for the french locale
 * selection. The generated locale-specific properties files contain the same
 * key-value pairs as the properties file of the initial plug-in.
 *
 */
public class NLSFragmentGenerator {
    public static final String PLUGIN_NAME_MACRO = "${plugin_name}"; //$NON-NLS-1$
    public static final String LOCALE_NAME_MACRO = "${locale}"; //$NON-NLS-1$

    private static final String HTML_EXTENSION = ".html"; //$NON-NLS-1$
    private static final String XML_EXTENSION = ".xml"; //$NON-NLS-1$
    private static final String CLASS_EXTENSION = ".class"; //$NON-NLS-1$
    private static final String JAVA_EXTENSION = ".java"; //$NON-NLS-1$
    private static final String PROPERTIES_EXTENSION = ".properties"; //$NON-NLS-1$
    private static final String JAR_EXTENSION = ".jar"; //$NON-NLS-1$

    private static final String BIN = "/bin/"; //$NON-NLS-1$
    private static final String EMPTY_STRING = ""; //$NON-NLS-1$
    private static final String BACKSLASH = "\\"; //$NON-NLS-1$
    private static final String SLASH = "/"; //$NON-NLS-1$
    private static final String RESOURCE_FOLDER_PARENT = "/nl"; //$NON-NLS-1$
    private static final double LATEST_ECLIPSE_VERSION = 3.4;

    private static final String ZERO = "0"; //$NON-NLS-1$
    private static final String PERIOD = "."; //$NON-NLS-1$
    private static final String MIN_MINOR = ZERO;
    private static final String MAX_MINOR = "9"; //$NON-NLS-1$
    private static final String LEFT_SQUARE_BRACKET = "["; //$NON-NLS-1$
    private static final String RIGHT_PARENTHESIS = ")"; //$NON-NLS-1$
    private static final String DEFAULT_VERSION = "1.0.0"; //$NON-NLS-1$
    private static final String VERSION_FORMAT_WITH_QUALIFIER = "\\d+\\.\\d+\\.\\d+\\..+"; //$NON-NLS-1$
    private static final String LOCALE_INFIX_SEPERATOR = "_"; //$NON-NLS-1$

    private final IWizardContainer container;
    private final String template;
    private final List plugins;
    private final List locales;
    private final boolean overwriteWithoutAsking;
    private IProgressMonitor monitor;

    private final Filters resourceFilter = new Filters(false) {
        {
            add(new AbstractFilter(false) {
                public boolean matches(Object object) {
                    String resource = object.toString();
                    return resource.endsWith(PROPERTIES_EXTENSION) || resource.endsWith(CLASS_EXTENSION)
                            || resource.endsWith(JAVA_EXTENSION);
                }
            });

            add(new AbstractFilter(false) {
                public boolean matches(Object object) {
                    String path = object.toString();
                    return path.indexOf(BIN) != -1 || path.endsWith(SLASH)
                            || path.endsWith(ICoreConstants.PLUGIN_FILENAME_DESCRIPTOR);
                }
            });

            add(new AbstractFilter(true) {
                public boolean matches(Object object) {
                    String path = object.toString();
                    return path.endsWith(XML_EXTENSION) || path.endsWith(HTML_EXTENSION);
                }
            });
        }
    };

    private final Filters propertiesFilter = new Filters(false) {
        {
            add(new AbstractFilter(false) {
                public boolean matches(Object object) {
                    String path = object.toString();
                    return path.indexOf(BIN) != -1 || path.endsWith(ICoreConstants.BUILD_FILENAME_DESCRIPTOR);
                }
            });

            add(new AbstractFilter(true) {
                public boolean matches(Object object) {
                    return object.toString().endsWith(PROPERTIES_EXTENSION);
                }
            });
        }
    };

    public NLSFragmentGenerator(String template, List plugins, List locales, IWizardContainer container,
            boolean overwriteWithoutAsking) {
        this.plugins = plugins;
        this.locales = locales;
        this.container = container;
        this.template = template;
        this.overwriteWithoutAsking = overwriteWithoutAsking;
    }

    private synchronized void setProgressMonitor(IProgressMonitor monitor) {
        this.monitor = monitor;
    }

    private synchronized IProgressMonitor getProgressMonitor() {
        return monitor;
    }

    public boolean generate() {
        try {
            final Map overwrites = promptForOverwrite(plugins, locales);

            container.run(false, false, new IRunnableWithProgress() {
                public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
                    setProgressMonitor(monitor);
                    try {
                        internationalizePlugins(plugins, locales, overwrites);
                    } catch (final Exception ex) {
                        Display.getDefault().syncExec(new Runnable() {
                            public void run() {
                                MDEPlugin.logException(ex, ex.getMessage(),
                                        MDEUIMessages.InternationalizeWizard_NLSFragmentGenerator_errorMessage);
                            }
                        });
                    }
                }
            });
        } catch (Exception e) {
            MDEPlugin.logException(e);
        }
        return true;
    }

    /**
     * Creates an NL fragment project along with the locale specific properties
     * files.
     * @throws CoreException 
     * @throws IOException 
     * @throws InvocationTargetException 
     * @throws InterruptedException 
     */
    private void internationalizePlugins(List plugins, List locales, Map overwrites)
            throws CoreException, IOException, InvocationTargetException, InterruptedException {

        Set created = new HashSet();

        for (Iterator it = plugins.iterator(); it.hasNext();) {
            IMonitorModelBase plugin = (IMonitorModelBase) it.next();

            for (Iterator iter = locales.iterator(); iter.hasNext();) {
                Locale locale = (Locale) iter.next();

                IProject project = getNLProject(plugin, locale);
                if (created.contains(project) || overwriteWithoutAsking || !project.exists()
                        || OVERWRITE == overwrites.get(project.getName())) {
                    if (!created.contains(project) && project.exists()) {
                        project.delete(true, getProgressMonitor());
                    }

                    if (!created.contains(project)) {
                        createNLFragment(plugin, project, locale);
                        created.add(project);
                        project.getFolder(RESOURCE_FOLDER_PARENT).create(false, true, getProgressMonitor());
                    }

                    project.getFolder(RESOURCE_FOLDER_PARENT).getFolder(locale.toString()).create(true, true,
                            getProgressMonitor());
                    createLocaleSpecificPropertiesFile(project, plugin, locale);
                }
            }
        }
    }

    private Object OVERWRITE = new Object();

    private Map promptForOverwrite(List plugins, List locales) {
        Map overwrites = new HashMap();

        if (overwriteWithoutAsking)
            return overwrites;

        for (Iterator iter = plugins.iterator(); iter.hasNext();) {
            IMonitorModelBase plugin = (IMonitorModelBase) iter.next();
            for (Iterator it = locales.iterator(); it.hasNext();) {
                Locale locale = (Locale) it.next();
                IProject project = getNLProject(plugin, locale);

                if (project.exists() && !overwrites.containsKey(project.getName())) {
                    boolean overwrite = MessageDialog.openConfirm(MDEPlugin.getActiveWorkbenchShell(),
                            MDEUIMessages.InternationalizeWizard_NLSFragmentGenerator_overwriteTitle,
                            NLS.bind(MDEUIMessages.InternationalizeWizard_NLSFragmentGenerator_overwriteMessage,
                                    pluginName(plugin, locale)));
                    overwrites.put(project.getName(), overwrite ? OVERWRITE : null);
                }
            }
        }

        return overwrites;
    }

    private IProject getNLProject(final IMonitorModelBase name, final Locale locale) {
        return ResourcesPlugin.getWorkspace().getRoot().getProject(pluginName(name, locale));
    }

    /**
     * Creates a fragment project for the specified plug-in and populates
     * the field data.
     * @param plugin
     * @throws CoreException 
     * @throws InvocationTargetException 
     * @throws InterruptedException 
     */
    private void createNLFragment(final IMonitorModelBase plugin, final IProject project, final Locale locale)
            throws CoreException, InvocationTargetException, InterruptedException {
        FragmentFieldData fragmentData = populateFieldData(plugin, locale);

        IProjectProvider projectProvider = new IProjectProvider() {
            public String getProjectName() {
                return project.getName();
            }

            public IProject getProject() {
                return project;
            }

            public IPath getLocationPath() {
                return project.getLocation();
            }
        };

        new NewProjectCreationOperation(fragmentData, projectProvider, null).run(getProgressMonitor());
    }

    private String pluginName(IMonitorModelBase plugin, Locale locale) {
        return template.replaceAll(quote(PLUGIN_NAME_MACRO), plugin.getMonitorBase().getId())
                .replaceAll(quote(LOCALE_NAME_MACRO), locale.toString());
    }

    /**
     * The fields are populated based on the plug-in attributes. Some fields
     * are set to their default values.
     */
    private FragmentFieldData populateFieldData(IMonitorModelBase plugin, Locale locale) {
        FragmentFieldData fragmentData = new FragmentFieldData();

        fragmentData.setId(pluginName(plugin, locale));
        fragmentData.setVersion(DEFAULT_VERSION);
        fragmentData.setMatch(0);

        fragmentData.setPluginId(plugin.getMonitorBase().getId());
        fragmentData.setPluginVersion(incrementRelease(plugin.getMonitorBase().getVersion()));
        fragmentData.setName(pluginName(plugin, locale) + " Fragment"); //$NON-NLS-1$
        fragmentData.setProvider(EMPTY_STRING);
        fragmentData.setSimple(true);

        if (!(plugin instanceof ExternalMonitorModelBase)) {
            fragmentData.setSourceFolderName("src"); //$NON-NLS-1$
            fragmentData.setOutputFolderName("bin"); //$NON-NLS-1$
        }

        fragmentData.setLegacy(false);
        fragmentData.setTargetVersion(
                Double.toString(ensureTargetVersionCompatibility(TargetPlatformHelper.getTargetVersion())));
        fragmentData.setHasBundleStructure(true);
        fragmentData.setOSGiFramework(null);
        fragmentData.setWorkingSets(null);

        return fragmentData;
    }

    /**
     * Adjusts the plug-in's version to reflect the required
     * fragment-host bundle-version range. For example, 
     * fragment-host's bundle-version range would be: "[1.0.0, 1.1.0)" 
     * if the host's version is 1.0.0
     * @param oldVersion
     * @return adjusted plug-in version
     */
    private String incrementRelease(String oldVersion) {
        if (oldVersion.matches(VERSION_FORMAT_WITH_QUALIFIER)) {
            oldVersion = oldVersion.substring(0, oldVersion.lastIndexOf(PERIOD));
        }

        String newVersion = LEFT_SQUARE_BRACKET + oldVersion + ',';
        String oldMinor = oldVersion.substring(oldVersion.indexOf(PERIOD) + 1, oldVersion.lastIndexOf(PERIOD));

        if (oldMinor.compareTo(MAX_MINOR) == 0) {
            String major = Integer
                    .toString(Integer.parseInt(oldVersion.substring(0, oldVersion.indexOf(PERIOD))) + 1);
            newVersion += major + PERIOD + MIN_MINOR + PERIOD + ZERO + RIGHT_PARENTHESIS;
        } else {
            String major = oldVersion.substring(0, oldVersion.indexOf(PERIOD));
            String newMinor = Integer.toString(Integer.parseInt(oldMinor) + 1);
            newVersion += major + PERIOD + newMinor + PERIOD + ZERO + RIGHT_PARENTHESIS;
        }

        return newVersion;
    }

    /**
     * Creates a locale specific properties file within the fragment project 
     * based on the content of the host plug-in's properties file.
     * @param fragmentProject
     * @param locale
     * @throws CoreException 
     * @throws IOException 
     */
    private void createLocaleSpecificPropertiesFile(final IProject fragmentProject, IMonitorModelBase plugin,
            final Locale locale) throws CoreException, IOException {
        final IFolder localeResourceFolder = fragmentProject.getFolder(RESOURCE_FOLDER_PARENT)
                .getFolder(locale.toString());

        //Case 1: External plug-in
        if (plugin instanceof ExternalMonitorModelBase) {
            final String installLocation = plugin.getInstallLocation();
            //Case 1a: External plug-in is a jar file
            if (installLocation.endsWith(JAR_EXTENSION)) {
                ZipFile zf = new ZipFile(installLocation);
                for (Enumeration e = zf.entries(); e.hasMoreElements();) {
                    worked();

                    ZipEntry zfe = (ZipEntry) e.nextElement();
                    String name = zfe.getName();

                    String[] segments = name.split(SLASH);
                    IPath path = Path.fromPortableString(join(SLASH, segments, 0, segments.length - 1));
                    String resourceName = segments[segments.length - 1];
                    String localizedResourceName = localeSpecificName(resourceName, locale);
                    if (propertiesFilter.include(name)) {

                        createParents(fragmentProject, path);
                        IFile file = fragmentProject.getFile(path.append(localizedResourceName));
                        InputStream is = zf.getInputStream(zfe);
                        file.create(is, false, getProgressMonitor());
                    } else if (resourceFilter.include(name)) {
                        IPath target = localeResourceFolder.getFullPath().append(path).append(resourceName);
                        createParents(fragmentProject, target.removeLastSegments(1).removeFirstSegments(1));
                        IFile file = fragmentProject.getFile(target.removeFirstSegments(1));
                        file.create(zf.getInputStream(zfe), false, getProgressMonitor());
                    }
                }
            }
            //Case 1b: External plug-in has a folder structure
            else {
                Visitor visitor = new Visitor() {
                    public void visit(File file) throws CoreException, FileNotFoundException {
                        worked();

                        String relativePath = file.getAbsolutePath().substring(installLocation.length())
                                .replaceAll(File.separator, SLASH);
                        String[] segments = relativePath.split(SLASH);
                        IPath path = Path.fromPortableString(join(SLASH, segments, 0, segments.length - 1));
                        String resourceName = segments[segments.length - 1];
                        String localizedResourceName = localeSpecificName(resourceName, locale);

                        if (propertiesFilter.include(relativePath + (file.isDirectory() ? SLASH : EMPTY_STRING))) {
                            createParents(fragmentProject, path);
                            IFile iFile = fragmentProject.getFile(path.append(localizedResourceName));
                            iFile.create(new FileInputStream(file), false, getProgressMonitor());
                        } else if (resourceFilter
                                .include(relativePath + (file.isDirectory() ? SLASH : EMPTY_STRING))) {
                            IPath target = localeResourceFolder.getFullPath().append(relativePath);
                            createParents(fragmentProject, target.removeLastSegments(1).removeFirstSegments(1));
                            IFile iFile = fragmentProject.getFile(target.removeFirstSegments(1));
                            iFile.create(new FileInputStream(file), false, getProgressMonitor());
                        }

                        if (file.isDirectory()) {
                            File[] children = file.listFiles();
                            for (int i = 0; i < children.length; i++) {
                                visit(children[i]);
                            }
                        }
                    }
                };

                visitor.visit(new File(installLocation));
            }
        }
        //Case 2: Workspace plug-in
        else {
            final IProject project = plugin.getUnderlyingResource().getProject();

            project.accept(new IResourceVisitor() {
                public boolean visit(IResource resource) throws CoreException {
                    worked();

                    IPath parent = resource.getFullPath().removeLastSegments(1).removeFirstSegments(1);
                    if (propertiesFilter.include(resource)) {
                        String segment = localeSpecificName(resource.getFullPath().lastSegment(), locale);
                        IPath fragmentResource = fragmentProject.getFullPath().append(parent).append(segment);

                        createParents(fragmentProject, parent);
                        resource.copy(fragmentResource, true, getProgressMonitor());
                    } else if (resourceFilter.include(resource)) {
                        IPath target = localeResourceFolder.getFullPath().append(parent)
                                .append(resource.getFullPath().lastSegment());
                        createParents(fragmentProject, target.removeLastSegments(1).removeFirstSegments(1));
                        resource.copy(target, true, getProgressMonitor());
                    }
                    return true;
                }
            });
        }

    }

    private void worked() {
        Shell shell = container.getShell();
        Display display = shell.getDisplay();
        if (display != null && !shell.isDisposed()) {
            display.readAndDispatch();
        }
    }

    private void createParents(IProject fragmentProject, IPath parent) throws CoreException {
        String[] segments = parent.segments();
        String path = new String();

        for (int i = 0; i < segments.length; i++) {
            path += SLASH + segments[i];
            IFolder folder = fragmentProject.getFolder(path);
            if (!folder.exists()) {
                folder.create(true, true, getProgressMonitor());
            }
        }
    }

    private String join(String delimiter, String[] parts) {
        return join(delimiter, parts, 0, parts.length);
    }

    private String join(String delimiter, String[] parts, int offset, int n) {
        StringBuffer builder = new StringBuffer();
        for (int i = offset; i < n; i++) {
            builder.append(parts[i]);
            if (i < parts.length - 1) {
                builder.append(delimiter);
            }
        }
        return builder.toString();
    }

    private String localeSpecificName(String name, Locale locale) {
        String[] parts = name.split(BACKSLASH + PERIOD);
        parts[0] = parts[0] + LOCALE_INFIX_SEPERATOR + locale;
        return join(PERIOD, parts);
    }

    private static class Filters {
        private final List filters = new LinkedList();
        private final boolean default_;

        public Filters(boolean default_) {
            this.default_ = default_;
        }

        public void add(Filter filter) {
            filters.add(filter);
        }

        public boolean include(Object object) {
            if (object instanceof IResource) {
                IResource resource = (IResource) object;
                IPath path = IResource.FILE == resource.getType() ? resource.getFullPath()
                        : resource.getFullPath().addTrailingSeparator();
                object = path.toPortableString();
            }

            for (Iterator iter = filters.iterator(); iter.hasNext();) {
                Filter filter = (Filter) iter.next();
                if (filter.matches(object)) {
                    return filter.inclusive();
                }
            }
            return default_;
        }
    }

    private static abstract class AbstractFilter implements Filter {

        private final boolean inclusive;

        public AbstractFilter(boolean inclusive) {
            this.inclusive = inclusive;

        }

        public boolean inclusive() {
            return inclusive;
        }
    }

    private static interface Filter {
        boolean inclusive();

        boolean matches(Object object);
    }

    private static interface Visitor {
        void visit(File file) throws CoreException, FileNotFoundException;
    }

    private String quote(String pattern) {
        return "\\Q" + pattern + "\\E"; //$NON-NLS-1$ //$NON-NLS-2$
    }

    /**
     * Ensures that the target version is compatible.
     * @param targetVersion
     * @return target version
     */
    private double ensureTargetVersionCompatibility(double targetVersion) {
        if (targetVersion < 3.0) {
            return LATEST_ECLIPSE_VERSION;
        }
        return targetVersion;
    }
}