com.gwtplatform.plugin.wizard.MergeLocalesWizard.java Source code

Java tutorial

Introduction

Here is the source code for com.gwtplatform.plugin.wizard.MergeLocalesWizard.java

Source

/**
 * Copyright 2011 IMAGEM Solutions TI sant
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.gwtplatform.plugin.wizard;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.ui.INewWizard;
import org.eclipse.ui.IWorkbench;

import com.gwtplatform.plugin.Activator;

/**
 * @author Michael Renaud
 */
public class MergeLocalesWizard extends Wizard implements INewWizard {

    private static final String DEPRACATED_COMMENT = "# TODO: DEPRECATED (CONSIDER REMOVING)\n";
    private static final String TRANSLATE_COMMENT = "# TODO: TRANSLATE\n";
    private static final String CONFIRM_COMMENT = "# TODO: CONFIRM TRANSLATION (DESCRIPTION CHANGED)\n";

    private MergeLocalesWizardPage page;
    private IStructuredSelection selection;
    private boolean isDone;

    //private boolean deprecatedCommentIssued;
    //private boolean translateCommentIssued;
    //private boolean confirmCommentIssued;

    public MergeLocalesWizard() {
        super();
        setNeedsProgressMonitor(true);
        setWindowTitle("Merge Locales");
    }

    @Override
    public void addPages() {
        page = new MergeLocalesWizardPage(selection);
        addPage(page);
    }

    @Override
    public void init(IWorkbench workbench, IStructuredSelection selection) {
        this.selection = selection;
    }

    @Override
    public boolean performFinish() {
        try {
            super.getContainer().run(false, false, new IRunnableWithProgress() {
                @Override
                public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
                    isDone = finish(monitor);
                }
            });
        } catch (Exception e) {
            return false;
        }
        return isDone;
    }

    /**
     * Takes every property file in extrasDir and merge them to the default locale
     * file in resourcesDir. Then it merges this resource file with every other
     * non-default local file in the directory.
     *
     * @return true on success, false on exception.
     */
    protected boolean finish(IProgressMonitor desiredMonitor) {
        IProgressMonitor monitor = desiredMonitor;
        if (monitor == null) {
            monitor = new NullProgressMonitor();
        }

        try {
            IContainer extrasDir = page.getExtraDir();
            IContainer resourcesDir = page.getResourcesDir();

            List<IResource> extraResources = enumeratePropertyFiles(extrasDir);
            PropertyCollection incomingProperties = new PropertyCollection();
            for (IResource extraResource : extraResources) {
                File extraFile = new File(extraResource.getLocationURI());
                if (findLocale(extraFile).isEmpty()) {
                    PropertyCollection newProperties = new PropertyCollection();
                    newProperties.readPropertiesFromFile(extraFile);
                    incomingProperties.mergeWith(newProperties, false, false);
                }
            }

            List<IResource> resourceResources = enumeratePropertyFiles(resourcesDir);
            IResource defaultLocaleResource = null;
            File defaultLocaleFile = null;
            for (IResource resourceResource : resourceResources) {
                File resourceFile = new File(resourceResource.getLocationURI());
                if (findLocale(resourceFile).isEmpty()) {
                    defaultLocaleResource = resourceResource;
                    defaultLocaleFile = resourceFile;
                    break;
                }
            }

            if (defaultLocaleFile == null) {
                IFile localeFile = resourcesDir.getFile(new Path("LocalizableResource.properties"));
                localeFile.create(new ByteArrayInputStream("".getBytes()), false, new NullProgressMonitor());
                localeFile.setCharset(localeFile.getProject().getDefaultCharset(), new NullProgressMonitor());

                defaultLocaleResource = localeFile;
                defaultLocaleFile = new File(localeFile.getLocationURI());
            }

            PropertyCollection defaultLocaleProperties = new PropertyCollection();
            defaultLocaleProperties.readPropertiesFromFile(defaultLocaleFile);
            defaultLocaleProperties.mergeWith(incomingProperties, true, false);

            IFile file = (IFile) defaultLocaleResource.getAdapter(IFile.class);
            file.setContents(new ByteArrayInputStream(defaultLocaleProperties.toString().getBytes()), false, true,
                    new NullProgressMonitor());

            for (IResource resourceResource : resourceResources) {
                File resourceFile = new File(resourceResource.getLocationURI());
                if (!findLocale(resourceFile).isEmpty()) {
                    PropertyCollection otherLocaleProperties = new PropertyCollection();
                    otherLocaleProperties.readPropertiesFromFile(resourceFile);
                    otherLocaleProperties.mergeWith(defaultLocaleProperties, true, true);

                    file = (IFile) resourceResource.getAdapter(IFile.class);
                    file.setContents(new ByteArrayInputStream(otherLocaleProperties.toString().getBytes()), false,
                            true, new NullProgressMonitor());
                }
            }

            // TODO
            /*
             * if deprecatedCommentIssued: print(
             * "Deprecated translations found. Look for: '%s'." %
             * deprecatedComment.strip() ) if confirmCommentIssued: print(
             * "Some translations could require confirmation. Look for: '%s'." %
             * confirmComment.strip() ) if translateCommentIssued: print(
             * "Some properties need to be translated. Look for: '%s'." %
             * translateComment.strip() )
             */
        } catch (Exception e) {
            IStatus status = new Status(IStatus.ERROR, Activator.PLUGIN_ID,
                    "An unexpected error has happened. Close the wizard and retry.", e);

            ErrorDialog.openError(getShell(), null, null, status);
            return false;
        }
        return true;
    }

    /**
     * Looks in the specified directory for all property files, that is, files
     * ending in .properties.
     *
     * @param dir
     *          The directory to look in.
     * @return A list of files.
     * @throws CoreException
     */
    protected List<IResource> enumeratePropertyFiles(IContainer dir) throws CoreException {
        /*
         * List<File> propertyFiles = new ArrayList<File>(); IResource[] resources =
         * dir.members(IResource.FILE);
         *
         * for(IResource resource : resources) {
         * if(resource.getName().endsWith(".properties")) propertyFiles.add(new
         * File(resource.getLocationURI())); }
         *
         * return propertyFiles;
         */

        List<IResource> propertyResources = new ArrayList<IResource>();
        IResource[] resources = dir.members(IResource.FILE);

        for (IResource resource : resources) {
            if (resource.getName().endsWith(".properties")) {
                propertyResources.add(resource);
            }
        }

        return propertyResources;
    }

    /**
     * Identifies the locale given the filename of a property file. For example
     * file_fr.properties will return fr. If there is no locale in the filename,
     * returns the empty string.
     *
     * @param file
     *          The file from which to extract the locale.
     * @return The locale or the empty string for the default locale.
     */
    protected String findLocale(File file) {
        String name[] = file.getName().split("_");
        if (name.length == 1) {
            return ""; // Default locale when none is found
        }
        String local[] = name[name.length - 1].split("\\.");
        if (local[0].equals("default")) {
            return "";
        }
        return local[0];
    }

    /**
     * A property is a single element of translation.
     */
    public class Property {
        private String comments;
        private String key;
        private String value;

        public Property() {
            comments = null;
            key = null;
            value = null;
        }

        /**
         * Get the next property from a file. Throws a InvalidProperty exception if
         * the property is not correctly formatted. This method will replace the
         * comments, key and value of this Property object.
         *
         * @param reader
         *          The file object to read from.
         * @return true on success, false on EOF.
         */
        public boolean getFromFile(BufferedReader reader) {
            try {
                // Skip blank lines. Return false if EOF is reached
                String line = reader.readLine();
                if (line == null) {
                    return false;
                }
                while (line.isEmpty()) {
                    line = reader.readLine();
                    if (line == null) {
                        return false;
                    }
                }

                // Read the comment block
                comments = "";
                while (line.startsWith("#")) {
                    comments += line + "\n";
                    line = reader.readLine();
                }

                // Read the key/value
                int index = line.indexOf("=");
                if (index < 0) {
                    // Comment-only property
                    return true;
                }
                key = line.substring(0, index);
                value = line.substring(index + 1);
                while (value.endsWith("\\\n")) {
                    value += reader.readLine();
                }

                return true;
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }

        /**
         * Check if this property is marked as deprecated.
         *
         * @return true if it is marked as deprecated, false otherwise.
         */
        public boolean isDeprecated() {
            return comments.contains(DEPRACATED_COMMENT);
        }

        /**
         * Ensures that this property is not marked as deprecated, by removing any
         * such comment.
         */
        public void unsetDeprecated() {
            if (isDeprecated()) {
                comments.replaceAll(DEPRACATED_COMMENT, "");
            }
        }

        /**
         * Ensures that this property is marked as deprecated, by including an
         * appropriate comment.
         */
        public void setDeprecated() {
            if (!comments.contains(DEPRACATED_COMMENT)) {
                comments += DEPRACATED_COMMENT;
            }
            //deprecatedCommentIssued = true;
        }

        /**
         * Ensures that this property indicates that it requires translation, by
         * including an appropriate comment.
         */
        public void setTranslationNeeded() {
            if (!comments.contains(TRANSLATE_COMMENT)) {
                comments += TRANSLATE_COMMENT;
            }
            //translateCommentIssued = true;
        }

        /**
         * Ensures that this property indicates that its translation should be
         * confirmed, by including an appropriate comment.
         */
        public void setConfirmTranslation() {
            if (!comments.contains(CONFIRM_COMMENT)) {
                comments += CONFIRM_COMMENT;
            }
            //confirmCommentIssued = true;
        }

        /**
         * Check that the comment matches between both properties. The comments are
         * considered to match if they are exactly the same when the "# TODO:"
         * comments are removed.
         *
         * @param otherProperty
         *          The other property with which to compare.
         * @return true if the comment matches, False otherwise.
         */
        public boolean commentMatchs(Property otherProperty) {
            return stripToDoComments().equals(otherProperty.stripToDoComments());
        }

        /**
         * Returns a copy of the comments without the TODO comments.
         *
         * @return The stripped result.
         */
        public String stripToDoComments() {
            String currentComments = this.comments;

            String[] commentsToStrip = { DEPRACATED_COMMENT, TRANSLATE_COMMENT, CONFIRM_COMMENT };

            for (String commentToStrip : commentsToStrip) {
                int index = currentComments.indexOf(commentToStrip);
                while (index != -1) {
                    currentComments = currentComments.substring(0, index)
                            + currentComments.substring(index + commentToStrip.length());
                    index = currentComments.indexOf(commentToStrip);
                }
            }

            return currentComments;
        }

        /**
         * Clone this object.
         *
         * @return The cloned object
         */
        public Property clone() {
            Property clone = new Property();
            clone.setComments(comments);
            clone.setKey(key);
            clone.setValue(value);
            return clone;
        }

        @Override
        public String toString() {
            String str = comments;
            if (key != null) {
                str += key + "=" + value + "\n\n";
            }
            return str;
        }

        public void setComments(String comments) {
            this.comments = comments;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public void setValue(String value) {
            this.value = value;
        }

        public String getComments() {
            return comments;
        }

        public String getKey() {
            return key;
        }

        public String getValue() {
            return value;
        }
    }

    /**
     * A collection of Property object that can be merged or written to files.
     */
    public class PropertyCollection {
        public Map<String, Property> properties;
        public List<Property> orderedProperties;

        public PropertyCollection() {
            properties = new HashMap<String, Property>();
            orderedProperties = new ArrayList<Property>();
        }

        /**
         * Adds an object of type Property to the collection.
         *
         * @param property
         *          The Property object to add
         */
        public void add(Property property) {
            properties.put(property.getKey(), property);
            orderedProperties.add(property);
        }

        public Property get(String key) {
            return properties.get(key);
        }

        /**
         * Read all the properties from a given file.
         *
         * @param file
         *          The file to read properties from.
         */
        public void readPropertiesFromFile(File file) {
            try {
                BufferedReader reader = new BufferedReader(new FileReader(file));

                while (true) {
                    Property property = new Property();
                    if (!property.getFromFile(reader)) {
                        break;
                    }
                    property.unsetDeprecated();
                    add(property);
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }

        /**
         * Merge this collection with another one. Any key that is found in the
         * other collection but not in this one will be added. If the markDeprecated
         * parameter is true, any key that is found in this collection but not in
         * the other one will be marked as deprecated. If the markTranslation
         * parameter is true, comments will be added to indicate translations that
         * need to be performed.
         *
         * @param otherCollection
         *          The collection to merge into this one.
         * @param markDeprecated
         *          true if deprecated translations should be indicated, false
         *          otherwise.
         * @param markTranslation
         *          true means that "# TODO: TRANSLATE" and
         *          "# TODO: CONFIRM TRANSLATION" comments will be added when
         *          needed.
         */
        public void mergeWith(PropertyCollection otherCollection, boolean markDeprecated, boolean markTranslation) {
            // Bring properties over
            for (Property property : otherCollection.getProperties()) {
                if (property.getKey() == null) {
                    continue; // Don't merge comment-only properties
                }
                if (!containsKey(property.getKey())) {
                    Property propertyCopy = property.clone();
                    add(propertyCopy);
                    if (markTranslation) {
                        propertyCopy.setTranslationNeeded();
                    }
                } else if (!property.getComments().isEmpty()) {
                    // Empty comments mean non-UIBinder translations OR deprecated
                    // translations, skip them.
                    // Non-empty comments are copied over, with a confirmation comment if
                    // requested.
                    Property currentProperty = get(property.getKey());
                    if (!currentProperty.commentMatchs(property)) {
                        currentProperty.setComments(property.getComments());
                        if (markTranslation) {
                            currentProperty.setConfirmTranslation();
                        }
                    }
                }
            }

            // Mark deprecated properties
            if (markDeprecated) {
                for (Property property : getProperties()) {
                    if (property.getKey() == null) {
                        continue; // Don't consider comment-only properties
                    }
                    if (!otherCollection.containsKey(property.getKey())) {
                        property.setDeprecated();
                    }
                }
            }
        }

        private boolean containsKey(String key) {
            return properties.containsKey(key);
        }

        public Collection<Property> getProperties() {
            // return properties.values();
            return orderedProperties;
        }

        @Override
        public String toString() {
            String str = "";
            for (Property property : getProperties()) {
                str += property.toString();
            }
            return str;
        }
    }

}