edu.ku.brc.specify.tools.l10nios.StrLocalizerAppForiOS.java Source code

Java tutorial

Introduction

Here is the source code for edu.ku.brc.specify.tools.l10nios.StrLocalizerAppForiOS.java

Source

/* Copyright (C) 2015, University of Kansas Center for Research
 * 
 * Specify Software Project, specify@ku.edu, Biodiversity Institute,
 * 1345 Jayhawk Boulevard, Lawrence, Kansas, 66045, USA
 * 
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
package edu.ku.brc.specify.tools.l10nios;

import static edu.ku.brc.ui.UIRegistry.FRAME;
import static edu.ku.brc.ui.UIRegistry.GLASSPANE;
import static edu.ku.brc.ui.UIRegistry.MAINPANE;
import static edu.ku.brc.ui.UIRegistry.getResourceString;
import static edu.ku.brc.ui.UIRegistry.getTopWindow;
import static edu.ku.brc.ui.UIRegistry.register;
import static edu.ku.brc.ui.UIRegistry.setAppName;
import static edu.ku.brc.ui.UIRegistry.setTopWindow;
import static org.apache.commons.io.FilenameUtils.getBaseName;
import static org.apache.commons.io.FilenameUtils.getExtension;
import static org.apache.commons.io.FilenameUtils.getFullPath;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FileDialog;
import java.awt.Frame;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.swing.AbstractListModel;
import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.google.api.translate.Language;
import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.looks.plastic.Plastic3DLookAndFeel;
import com.jgoodies.looks.plastic.PlasticLookAndFeel;
import com.jgoodies.looks.plastic.theme.DesertBlue;
import com.jgoodies.looks.plastic.theme.ExperienceBlue;

import edu.ku.brc.af.core.FrameworkAppIFace;
import edu.ku.brc.af.core.MacOSAppHandler;
import edu.ku.brc.af.prefs.AppPreferences;
import edu.ku.brc.af.ui.forms.ResultSetController;
import edu.ku.brc.af.ui.forms.ResultSetControllerListener;
import edu.ku.brc.helpers.XMLHelper;
import edu.ku.brc.specify.Specify;
import edu.ku.brc.ui.ChooseFromListDlg;
import edu.ku.brc.ui.IconManager;
import edu.ku.brc.ui.JStatusBar;
import edu.ku.brc.ui.ToggleButtonChooserDlg;
import edu.ku.brc.ui.ToggleButtonChooserPanel;
import edu.ku.brc.ui.UIHelper;
import edu.ku.brc.ui.UIRegistry;
import edu.ku.brc.ui.dnd.GhostGlassPane;

/**
 * @author rods
 *
 * @code_status Alpha
 *
 * Jul 15, 2009
 *
 */
@SuppressWarnings("serial")
public class StrLocalizerAppForiOS extends JPanel implements FrameworkAppIFace, WindowListener {
    private static final Logger log = Logger.getLogger(StrLocalizerAppForiOS.class);

    protected JFrame frame = null;
    protected GhostGlassPane glassPane;

    protected File rootDir = null;
    protected String currentPath = null;
    protected Locale englishLocale = Locale.US;
    protected Locale destLanguage = null;

    protected boolean doSrcParsing = false;

    // Terms in a file
    protected JList termList;
    protected ItemModel model = null;

    // Files
    protected Vector<L10NFile> srcFiles = new Vector<L10NFile>();
    protected L10NFile currFile = null;
    protected JList fileList;
    protected DefaultListModel fileModel;

    protected JTextArea srcLbl;
    protected JTextArea comment;
    protected JLabel destLbl;
    protected JTextField textField;
    protected JButton prevBtn;
    protected JButton nxtBtn;
    protected JButton transBtn;
    protected JLabel fileLbl;
    protected JPanel mainPane;
    protected JStatusBar statusBar;

    // SearchBox

    protected JMenuItem startTransMenuItem;
    protected JMenuItem stopTransMenuItem;
    protected JMenuItem scanMI;

    protected L10NSrcIndexer srcIndexer;
    protected L10NUIIndexer uiIndexer;

    protected ResultSetController rsController;
    protected ResultSetControllerListener rscListener = null;
    protected int oldInx = -1;

    protected Vector<String> newKeyList = new Vector<String>();
    protected AtomicBoolean contTrans = new AtomicBoolean(true);

    protected boolean isSkippingIndexes = false;

    /**
     * 
     */
    public StrLocalizerAppForiOS() {
        super();

        new MacOSAppHandler(this);

    }

    /**
     * @return
     */
    public String[] getFileNames() {
        // These need to be moved to an XML file
        String[] fileNames = { "Localizable.strings", "MainStoryboard_iPad.strings", };
        return fileNames;
    }

    /**
     * 
     */
    public void startUp() {
        //AskForDirectory afd = new AskForDirectory(frame);
        //currentPath = afd.getDirectory();
        FileDialog dlg = new FileDialog(frame, "Select a Directory", FileDialog.LOAD);
        UIHelper.centerAndShow(dlg);
        String currentPath = dlg.getDirectory();

        if (StringUtils.isNotEmpty(currentPath)) {
            if (doSrcParsing) {
                File rootDir = new File("/Users/rods/Documents/SVN/SpecifyInsightL10N");
                srcIndexer = new L10NSrcIndexer(rootDir);
                doScanSources();
            }

            boolean isDirOK = true;
            rootDir = new File(currentPath);
            if (!rootDir.exists()) {
                String dirPath = getFullPath(currentPath);
                rootDir = new File(dirPath);
                isDirOK = rootDir.exists();
            }

            if (isDirOK) {
                createUI();

                register(MAINPANE, mainPane);
                frame.setGlassPane(glassPane = GhostGlassPane.getInstance());
                frame.setLocationRelativeTo(null);
                Toolkit.getDefaultToolkit().setDynamicLayout(true);
                register(GLASSPANE, glassPane);

                if (setupSrcFiles(rootDir) > 0) {
                    frame.pack();
                    UIHelper.centerAndShow(frame);

                    destLanguage = doChooseLangLocale(Locale.ENGLISH);
                    if (destLanguage != null) {
                        destLbl.setText(destLanguage.getDisplayName() + ":");
                        return;
                    }
                } else {
                    UIRegistry.showError("The are no localizable files in the directory you selected.");
                    System.exit(0);
                }
            }
        }

        UIRegistry.showError("StrLocalizer will exit.");
        System.exit(0);
    }

    /**
     * @param hideExisting
     * @return
     */
    private void appendLocale(final Vector<Locale> locales, final Vector<Locale> destLocales,
            final String language) {
        for (Locale l : locales) {
            if (l.getLanguage().equals(language) && StringUtils.isEmpty(l.getCountry())) {
                destLocales.add(l);
                break;
            }
        }
    }

    /**
     * @param hideExisting
     * @return
     */
    private Locale doChooseLangLocale(final Locale hideLocale) {
        Vector<Locale> locales = new Vector<Locale>();
        Collections.addAll(locales, Locale.getAvailableLocales());

        String[] langs = { "es", "sv", "sq", "fr" };
        Vector<Locale> destLocales = new Vector<Locale>();
        destLocales.add(Locale.GERMAN);
        for (String lang : langs) {
            appendLocale(locales, destLocales, lang);
        }

        Vector<String> localeNames = new Vector<String>();
        for (Locale l : destLocales) {
            localeNames.add(l.getDisplayName());
        }

        ToggleButtonChooserDlg<String> chooser = new ToggleButtonChooserDlg<String>((Frame) null, "CHOOSE_LOCALE",
                localeNames, ToggleButtonChooserPanel.Type.RadioButton);
        chooser.setUseScrollPane(true);
        UIHelper.centerAndShow(chooser);

        if (!chooser.isCancelled()) {
            return destLocales.get(chooser.getSelectedIndex());
        }
        return null;
    }

    /**
     * @param dirName
     */
    protected int setupSrcFiles(final File dir) {
        srcFiles.clear();
        String[] exts = { "strings" };
        Collection<?> files = FileUtils.listFiles(dir, exts, false);
        for (Object fobj : files) {
            File f = (File) fobj;
            L10NFile l10nFile = new L10NFile(new File(dir.getAbsolutePath() + File.separator + f.getName()));
            if (l10nFile.load()) {
                srcFiles.add(l10nFile);
            }
        }
        Collections.sort(srcFiles);

        fileModel = new DefaultListModel();
        for (L10NFile f : srcFiles) {
            fileModel.addElement(f);
        }
        fileList.setModel(fileModel);
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                fileList.setSelectedIndex(0);
            }
        });

        return srcFiles.size();
    }

    /**
     * @param langCode the language code - "es", "en" ,etc
     *   
     * Assumes srcLocaleFiles has already been setup.
     */
    //    protected void setupDestFiles(final String langCode, final String destPath)
    //    {
    //       destFiles.clear();
    //       for (StrLocaleFile f : srcFiles)
    //       {
    //          String newPath;
    //          if (destPath != null)
    //          {
    //             File duh = new File(f.getPath());
    //             String newName = duh.getName().replace("_" + srcLangCode + ".", "_" + langCode + ".");
    //             newPath = destPath + File.separator + newName;
    //          }
    //          else
    //          {
    //              String path = f.getPath();
    //                newPath = path.replace("_" + srcLangCode + ".", "_" + langCode + ".");
    //                newPath = newPath.replace(File.separator + srcLangCode + File.separator, File.separator + langCode + File.separator);
    //          }
    //          destFiles.add(new StrLocaleFile(newPath, f.getPath(), true));
    //       }
    //    }

    private JTextArea setTAReadOnly(final JTextArea textArea) {
        textArea.setEditable(false);
        textArea.setLineWrap(true);
        if (!UIHelper.isMacOS()) {
            textArea.setBorder(BorderFactory.createLineBorder(Color.BLACK));
            //textArea.setBackground(new Color(245,245,245));
        }
        return textArea;
    }

    /**
     * 
     */
    private void createUI() {
        IconManager.setApplicationClass(Specify.class);
        IconManager.loadIcons(XMLHelper.getConfigDir("icons_datamodel.xml")); //$NON-NLS-1$
        IconManager.loadIcons(XMLHelper.getConfigDir("icons_plugins.xml")); //$NON-NLS-1$
        IconManager.loadIcons(XMLHelper.getConfigDir("icons_disciplines.xml")); //$NON-NLS-1$

        System.setProperty("edu.ku.brc.ui.db.PickListDBAdapterFactory", //$NON-NLS-1$
                "edu.ku.brc.specify.tools.StrLocPickListFactory"); // Needed By the Auto Cosmplete UI  //$NON-NLS-1$

        CellConstraints cc = new CellConstraints();

        fileList = new JList(fileModel = new DefaultListModel());
        termList = new JList(model = new ItemModel(null));

        srcLbl = setTAReadOnly(UIHelper.createTextArea(3, 40));
        //srcLbl.setBorder(new LineBorder(srcLbl.getForeground()));
        textField = UIHelper.createTextField(40);

        comment = setTAReadOnly(UIHelper.createTextArea(3, 40));

        /*textField.getDocument().addDocumentListener(new DocumentAdaptor() {
        @Override
        protected void changed(DocumentEvent e)
        {
            hasChanged = true;
        }
        });*/

        statusBar = new JStatusBar();
        statusBar.setSectionText(1, "     "); //$NON-NLS-1$ //$NON-NLS-2$
        UIRegistry.setStatusBar(statusBar);

        srcLbl.setEditable(false);

        rsController = new ResultSetController(null, false, false, false, "", 1, true);

        transBtn = UIHelper.createButton(getResourceString("StrLocalizerApp.Translate"));
        transBtn.setVisible(false);

        PanelBuilder pbr = new PanelBuilder(new FormLayout("p,2px,f:p:g", "p,4px,p,4px,p,4px,p,4px,p,4px,p"));

        //pbr.add(UIHelper.createLabel(getResourceString("StrLocalizerApp.FileLbl")), cc.xy(1, 1));
        //fileLbl = UIHelper.createLabel("   ");
        //pbr.add(fileLbl, cc.xy(3, 1));

        int y = 1;
        pbr.addSeparator("Item", cc.xyw(1, y, 3));
        y += 2;

        pbr.add(UIHelper.createLabel("English:", SwingConstants.RIGHT), cc.xy(1, y));
        pbr.add(srcLbl, cc.xy(3, y));
        y += 2;

        pbr.add(UIHelper.createFormLabel("Comment", SwingConstants.RIGHT), cc.xy(1, y));
        pbr.add(comment, cc.xy(3, y));
        y += 2;

        destLbl = UIHelper.createFormLabel("", SwingConstants.RIGHT);//destLanguage.getDisplayName());
        pbr.add(destLbl, cc.xy(1, y));
        pbr.add(textField, cc.xy(3, y));
        y += 2;

        pbr.add(rsController.getPanel(), cc.xyw(1, y, 3));
        y += 2;
        pbr.add(transBtn, cc.xy(1, y));

        JScrollPane sp = UIHelper.createScrollPane(termList);

        PanelBuilder pb = new PanelBuilder(new FormLayout("f:p:g", "p,4px,f:p:g,10px,p"));
        pb.addSeparator("Localize", cc.xy(1, 1));
        pb.add(sp, cc.xy(1, 3));
        pb.add(pbr.getPanel(), cc.xy(1, 5));
        pb.setDefaultDialogBorder();

        ResultSetController.setBackStopRS(rsController);

        PanelBuilder fpb = new PanelBuilder(new FormLayout("8px,f:p:g", "p,4px,f:p:g"));
        JScrollPane filesp = UIHelper.createScrollPane(fileList);
        fpb.add(UIHelper.createLabel("Files", SwingConstants.CENTER), cc.xy(2, 1));
        fpb.add(filesp, cc.xy(2, 3));

        setLayout(new BorderLayout());
        add(fpb.getPanel(), BorderLayout.WEST);
        add(pb.getPanel(), BorderLayout.CENTER);
        add(statusBar, BorderLayout.SOUTH);

        mainPane = this;

        textField.addFocusListener(new FocusAdapter() {
            @Override
            public void focusLost(FocusEvent e) {
                checkForChange();
            }
        });

        termList.addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                if (!e.getValueIsAdjusting()) {
                    itemSelected();
                }
            }
        });

        fileList.addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                if (!e.getValueIsAdjusting()) {
                    fileSelected();
                }
            }
        });

        transBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String txt = srcLbl.getText();
                String newText = translate(txt);
                if (StringUtils.isNotEmpty(newText)) {
                    newText = newText.replace("&#039;", "'");
                    textField.setText(newText);
                    L10NItem entry = (L10NItem) termList.getSelectedValue();
                    entry.setValue(textField.getText());
                }
            }
        });

        rscListener = new ResultSetControllerListener() {
            @Override
            public void newRecordAdded() {
            }

            @Override
            public void indexChanged(int newIndex) {
                termList.setSelectedIndex(newIndex);
            }

            @Override
            public boolean indexAboutToChange(int oldIndex, int newIndex) {
                return true;
            }
        };

        rsController.addListener(rscListener);
    }

    /**
     * 
     */
    private void checkForChange() {
        if (oldInx > -1) {
            L10NItem entry = (L10NItem) model.getElementAt(oldInx);
            String oldText = entry.getValue();
            String newText = textField.getText();
            if (oldText == null || !oldText.equals(newText)) {
                entry.setValue(newText);
                entry.setChanged(true);
                setDataHasChanged();
            }
        }
    }

    /**
     * 
     */
    private void setDataHasChanged() {
        String title = frame.getTitle();
        if (!title.endsWith("*")) {
            frame.setTitle(title + " *");
        }
    }

    /**
     * 
     */
    private void fileSelected() {
        checkForChange();

        loadFileIntoList((L10NFile) fileList.getSelectedValue());
    }

    /**
     * 
     */
    private void itemSelected() {
        checkForChange();

        int inx = termList.getSelectedIndex();
        if (inx > -1) {
            transBtn.setEnabled(true);
            L10NItem srcEntry = (L10NItem) model.getElementAt(inx);
            //System.out.println(srcEntry.hashCode());

            srcLbl.setText(srcEntry.getTitle());

            String comStr = srcEntry.isUIComp() ? "" : srcEntry.getComment();
            if (StringUtils.isNotEmpty(comStr)) {
                comStr = StringUtils.remove(comStr, "/* ");
                comStr = StringUtils.remove(comStr, " */").trim();
                comStr = StringUtils.replace(comStr, " \n", "\n");
                comStr = StringUtils.replace(comStr, "\n  ", "\n");
                comStr = StringUtils.replace(comStr, "\n", ";\n");
            }
            comment.setText(comStr);

            String str = srcEntry.getValue();

            try {
                str = new String(str.getBytes("UTF-8"), "UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            textField.setText(str);//str != null ? str : srcEntry.getKey());

            rsController.removeListener(rscListener);
            rsController.setIndex(inx);
            rsController.addListener(rscListener);

            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    textField.requestFocus();
                    textField.selectAll();
                }
            });

        } else {
            srcLbl.setText("");
            textField.setText("");
            transBtn.setEnabled(false);
        }
        oldInx = inx;
    }

    /**
     * @param code
     * @return
     */
    private Language getLangFromCode(final String code) {
        if (code.equals("se"))
            return Language.SWEDISH;
        if (code.equals("es"))
            return Language.SPANISH;
        if (code.equals("pt"))
            return Language.PORTUGUESE;

        return Language.ENGLISH;
    }

    /**
     * @param inputText
     */
    protected String translate(final String inputText) {
        //        if (inputText.isEmpty()) return "";
        //        //System.out.println("\n"+inputText);
        //        
        //        Translate.setHttpReferrer("http://www.specifysoftware.org");
        //
        //        try
        //        {
        //            String text = inputText;
        //            
        //            boolean hasSpecialChars = false;
        //            while (StringUtils.contains(text, "%d") || 
        //                    StringUtils.contains(text, "%s") || 
        //                    StringUtils.contains(text, "\\n"))
        //            {
        //                text = StringUtils.replace(text, "%d", "99");
        //                text = StringUtils.replace(text, "%s", "88");
        //                text = StringUtils.replace(text, "\\n", " 77 ");
        //                hasSpecialChars = true;
        //            }
        //            
        //            Language lang = getLangFromCode(destLanguage.getCode());
        //            //System.out.println(text);
        //            String newText = Translate.execute(text, Language.ENGLISH, lang);
        //            
        //            if (hasSpecialChars)
        //            {
        //                while (StringUtils.contains(newText, "77") ||
        //                       StringUtils.contains(newText, "88") ||
        //                       StringUtils.contains(newText, "99"))
        //                {
        //                    newText = StringUtils.replace(newText, "99", "%d");
        //                    newText = StringUtils.replace(newText, "88", "%s");
        //                    newText = StringUtils.replace(newText, " 77 ", " \\n ");
        //                    newText = StringUtils.replace(newText, "77 ", "\\n ");
        //                    newText = StringUtils.replace(newText, " 77", " \\n");
        //                }
        //            }
        //            //System.out.println(newText);
        //            return newText;
        //            
        //        } catch (Exception e)
        //        {
        //            e.printStackTrace();
        //        }
        return null;
    }

    /**
     * 
     */
    private void translateNewItems() {
        //        //writeGlassPaneMsg(getResourceString("StrLocalizerApp.TranslatingNew"), 24);
        //        final String    STATUSBAR_NAME = "STATUS";
        //        final JStatusBar statusBar     = UIRegistry.getStatusBar();
        //        statusBar.setProgressRange(STATUSBAR_NAME, 0, 100);
        //        
        //        startTransMenuItem.setEnabled(false);
        //        stopTransMenuItem.setEnabled(true);
        //        
        //        final double total = newKeyList.size();
        //        
        //        SwingWorker<Integer, Integer> translator = new SwingWorker<Integer, Integer>()
        //        {
        //            @Override
        //            protected Integer doInBackground() throws Exception
        //            {
        //                int count = 0;
        //                for (String key : newKeyList)
        //                {
        //                    L10NItem entry = srcFile.getItemHash().get(key);
        //                    //if (StringUtils.contains(entry.getSrcStr(), "%") || StringUtils.contains(entry.getSrcStr(), "\n"))
        //                    {
        //                        String transText = translate(entry.getSrcStr());
        //                        if (transText != null)
        //                        {
        //                            entry.setDstStr(transText);
        //                            //writeGlassPaneMsg(String.format("%d / %d", count, newKeyList.size()), 18);
        //                            //System.out.println(String.format("%s - %d / %d", key, count, newKeyList.size()));
        //                        }
        //                    }
        //                    
        //                    try
        //                    {
        //                        Thread.sleep(100 + (int)(Math.random() * 100.0));
        //                    } catch (InterruptedException ex) {}
        //                    
        //                    setProgress((int)(((double)count/total)*100.0));
        //                    System.out.println(entry.getSrcStr()+"  "+count);
        //                    //glassPane.setProgress((int)( (100.0 * count) / total));
        //                    count++;
        //                    
        //                    if (!contTrans.get())
        //                    {
        //                        return null;
        //                    }
        //                }
        //                return null;
        //            }
        //
        //            /* (non-Javadoc)
        //             * @see javax.swing.SwingWorker#done()
        //             */
        //            @Override
        //            protected void done()
        //            {
        //                //glassPane.setProgress(100);
        //                //clearGlassPaneMsg();
        //                
        //                //statusBar.setIndeterminate(STATUSBAR_NAME, false);
        //                statusBar.setText("");
        //                statusBar.setProgressDone(STATUSBAR_NAME);
        //                
        //                UIRegistry.showLocalizedMsg("Done Localizing");
        //                
        //                startTransMenuItem.setEnabled(true);
        //                stopTransMenuItem.setEnabled(false);
        //                
        //
        //            }
        //        };
        //        
        //        statusBar.setIndeterminate(STATUSBAR_NAME, true);
        //        
        //        translator.addPropertyChangeListener(
        //                new PropertyChangeListener() {
        //                    public  void propertyChange(final PropertyChangeEvent evt) {
        //                        //System.out.println(evt.getPropertyName());
        //                        
        //                        if ("progress".equals(evt.getPropertyName())) 
        //                        {
        //                            statusBar.setText(String.format("%d / 100 ", (Integer)evt.getNewValue()) + "%");
        //                        }
        //                    }
        //                });
        //        translator.execute();
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.af.core.FrameworkAppIFace#doAbout()
     */
    @Override
    public void doAbout() {

    }

    /* (non-Javadoc)
     * @see edu.ku.brc.af.core.FrameworkAppIFace#doExit(boolean)
     */
    @Override
    public boolean doExit(boolean doAppExit) {
        if (doAppExit) {
            System.exit(0);
            return true;
        }
        return false;
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.af.core.FrameworkAppIFace#doPreferences()
     */
    @Override
    public void doPreferences() {
    }

    /**
     * 
     */
    //    private void doOpen()
    //    {
    //        FileDialog dlg = new FileDialog((JFrame)getTopWindow(), "Open a properties I18N File", FileDialog.LOAD);
    //        dlg.setFilenameFilter(new FilenameFilter(){
    //            public boolean accept(File dir, String name){
    //              return (name.endsWith(".properties"));
    //            }
    //         });
    //        dlg.setVisible(true);
    //        String fileName = dlg.getFile();
    //        if (fileName != null)
    //        {
    //            ChooseFromListDlg<LanguageEntry> ldlg = new ChooseFromListDlg<LanguageEntry>((Frame )getTopWindow(), 
    //                  getResourceString("StrLocalizerApp.ChooseLanguageDlgTitle"), languages);
    //            UIHelper.centerAndShow(ldlg);
    //            if (ldlg.isCancelled() || ldlg.getSelectedObject() == null)
    //            {
    //               return;
    //            }
    //            
    //            termList.clearSelection();
    //            newTermList.clearSelection();
    //            
    //
    //            
    //            String path = dlg.getDirectory() + File.separator + fileName;
    //            String srcPath = path.replace("_en.", "_" + ldlg.getSelectedObject().getCode() + ".");
    //            
    //            srcFile = new StrLocaleFile(path, null, false);
    //            destFile = new StrLocaleFile(srcPath, path, true);
    //            destLanguage = ldlg.getSelectedObject();
    //            final String newLang = destLanguage.getEnglishName();
    //            SwingUtilities.invokeLater(new Runnable() {
    //
    //            /* (non-Javadoc)
    //             * @see java.lang.Runnable#run()
    //             */
    //            @Override
    //            public void run()
    //            {
    //               destLbl.setText(newLang);
    //            }
    //               
    //            });
    //            newKeyList.clear();
    //            
    //            mergeToSrc(srcFile, destFile);
    //            
    //            termList.setModel(new ItemModel(srcFile));
    //            DefaultListModel model = new DefaultListModel();
    //            model.clear();
    //            for (String str : newKeyList)
    //            {
    //                model.addElement(str);
    //            }
    //            newTermList.setModel(model);
    //        }
    //    }

    /**
     * 
     */
    private void doSave() {
        termList.clearSelection();

        boolean isOK = true;
        for (L10NFile file : srcFiles) {
            if (!file.save()) {
                isOK = false;
            }
        }

        if (isOK) {
            String title = frame.getTitle();
            if (title.endsWith("*")) {
                frame.setTitle(title.substring(0, title.length() - 2));
            }
        }
    }

    /**
     * @param frame
     */
    public void addMenuBar(final JFrame frame) {
        this.frame = frame;

        JMenuBar menuBar = new JMenuBar();

        JMenu fileMenu = new JMenu(getResourceString("FILE"));

        JMenuItem chooseFileItem = new JMenuItem(getResourceString("StrLocalizerApp.ChooseFileMenu"));
        fileMenu.add(chooseFileItem);

        chooseFileItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                doChooseFile();
            }
        });

        JMenuItem saveItem = new JMenuItem(getResourceString("SAVE"));
        fileMenu.add(saveItem);

        saveItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                doSave();
            }
        });

        if (!UIHelper.isMacOS()) {
            fileMenu.addSeparator();
            JMenuItem exitMenu = new JMenuItem(getResourceString("EXIT"));
            exitMenu.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    doExit(checkForChanges());
                }
            });

            fileMenu.add(exitMenu);
        }
        menuBar.add(fileMenu);

        JMenu scanMenu = new JMenu(getResourceString("Scan"));
        menuBar.add(scanMenu);

        scanMI = new JMenuItem(getResourceString("Source Code"));
        scanMenu.add(scanMI);

        scanMI.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                scanSources();
            }
        });

        JMenu transMenu = new JMenu(getResourceString("StrLocalizerApp.Translate"));
        menuBar.add(transMenu);

        startTransMenuItem = new JMenuItem(getResourceString("StrLocalizerApp.Start"));
        transMenu.add(startTransMenuItem);

        startTransMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                translateNewItems();
            }
        });
        transMenu.setVisible(false);

        stopTransMenuItem = new JMenuItem(getResourceString("Stop"));
        transMenu.add(stopTransMenuItem);

        stopTransMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                contTrans.set(false);
            }
        });
        stopTransMenuItem.setEnabled(false);

        frame.setJMenuBar(menuBar);

        setTopWindow(frame);

        register(FRAME, frame);
    }

    /**
     * @return true if no unsaved changes are present
     * else return results of prompt to save
     */
    protected boolean checkForChanges() {
        //        if (termList != null)
        //        {
        //            int s = termList.getSelectedIndex();
        //            if (s != -1)
        //            {
        //               L10NItem entry = srcFile.getKey(s);
        //               entry.setDstStr(textField.getText());
        //            }
        //            if (srcFile.isEdited())
        //            {
        //               int response = JOptionPane.showOptionDialog((Frame )getTopWindow(), 
        //                     String.format(getResourceString("StrLocalizerApp.SaveChangesMsg"), destFile.getPath()), 
        //                     getResourceString("StrLocalizerApp.SaveChangesTitle"), 
        //                     JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, JOptionPane.YES_OPTION);
        //               if (response == JOptionPane.CANCEL_OPTION)
        //               {
        //                  return false;
        //               }
        //               if (response == JOptionPane.YES_OPTION)
        //               {
        //                  doSave(); 
        //                  return true; //what if it fails? 
        //               }
        //            }
        //            return true;
        //        }

        boolean hasChanges = false;
        for (L10NFile f : srcFiles) {
            if (f.isChanged()) {
                hasChanges = true;
                break;
            }
        }

        if (hasChanges) {
            int response = JOptionPane.showOptionDialog((Frame) getTopWindow(),
                    "Changes have not been saved.\n\nDo you wish to save them?",
                    getResourceString("StrLocalizerApp.SaveChangesTitle"), JOptionPane.YES_NO_CANCEL_OPTION,
                    JOptionPane.QUESTION_MESSAGE, null, null, JOptionPane.YES_OPTION);
            if (response == JOptionPane.CANCEL_OPTION) {
                return false;
            }
            if (response == JOptionPane.YES_OPTION) {
                doSave();
                return true; //what if it fails? 
            }
        }
        return true;
    }

    private void doScanSources() {
        srcIndexer.startIndexing();
        srcIndexer.startSearching();
    }

    private void scanSources() {
        scanMI.setEnabled(false);

        final String STATUSBAR_NAME = "STATUS";
        final JStatusBar statusBar = UIRegistry.getStatusBar();
        statusBar.setProgressRange(STATUSBAR_NAME, 0, 100);

        SwingWorker<Integer, Integer> translator = new SwingWorker<Integer, Integer>() {
            @Override
            protected Integer doInBackground() throws Exception {
                doScanSources();
                return null;
            }

            @Override
            protected void done() {
                scanMI.setEnabled(false);
            }
        };

        translator.addPropertyChangeListener(new PropertyChangeListener() {
            public void propertyChange(final PropertyChangeEvent evt) {
                if ("progress".equals(evt.getPropertyName())) {
                    statusBar.setText(String.format("%d / 100 ", (Integer) evt.getNewValue()) + "%");
                }
            }
        });
        translator.execute();
    }

    /**
     * Open another properties file for translation
     */
    protected void doChooseFile() {
        if (!checkForChanges()) {
            //return;
        }

        ChooseFromListDlg<L10NFile> ldlg = new ChooseFromListDlg<L10NFile>((Frame) getTopWindow(),
                getResourceString("StrLocalizerApp.ChooseFileDlgTitle"), srcFiles);
        UIHelper.centerAndShow(ldlg);
        if (ldlg.isCancelled() || ldlg.getSelectedObject() == null) {
            return;
        }

        if (destLanguage == null) {
            destLanguage = doChooseLangLocale(Locale.ENGLISH);
            if (destLanguage != null) {
                destLbl.setText(destLanguage.getDisplayName() + ":");
                loadFileIntoList(ldlg.getSelectedObject());

                frame.pack();
            } else {
                System.exit(0);
            }
        }
    }

    /**
     * @param file
     */
    protected void loadFileIntoList(final L10NFile file) {
        termList.clearSelection();
        model = new ItemModel(file);
        termList.setModel(model);
        rsController.setLength(file.getItems().size());
    }

    /**
     * @param newSrc
     */
    /*protected void newSrcFile(final StrLocaleFile newSrc)
    {
    termList.clearSelection();
    newTermList.clearSelection();
        
    srcFile = newSrc;
        
    // re-create and reload the dest file to clear unsaved changes and/or states
    int           destInx = getLocaleFileInxBySrcPath(destFiles, srcFile.getPath());
    StrLocaleFile oldDest = destFiles.get(destInx);
    destFile = new StrLocaleFile(oldDest.getPath(), oldDest.getSrcPath(), true);
    destFiles.set(destInx, destFile);
        
    newKeyList.clear();
        
    mergeToSrc(srcFile, destFile);
        
    ItemModel termsModel = new ItemModel(srcFile);
    termList.setModel(termsModel);
    DefaultListModel itemModel = new DefaultListModel();
    itemModel.clear();
    for (String str : newKeyList)
    {
        itemModel.addElement(str);
    }
    newTermList.setModel(itemModel);
    rsController.setLength(srcFile.getNumberOfKeys());
        
    SwingUtilities.invokeLater(new Runnable() {
     @Override
     public void run()
     {
        fileLbl.setText(destFile.getPath());            
     }
    });
        
    searchable = new LocalizerSearchHelper(baseDir, "props-index");
    searchable.indexProps(srcFile.getItems());
    }*/

    /**
     * @param locale
     * @return
     */
    //    private String getFullLang(final Locale locale)
    //    {
    //        return locale.getLanguage() + (StringUtils.isNotEmpty(locale.getVariant()) ? ("_"+locale.getVariant()): "");
    //    }

    /**
     * @param hideExisting
     * @return
     */
    //    private Locale doChooseLangLocale(final boolean hideExisting)
    //    {
    //
    //        HashSet<String> existingLocs = new HashSet<String>();
    //        if (hideExisting)
    //        {
    //            for (String nm : rootDir.list())
    //            {
    //                if (!nm.startsWith("."))
    //                {
    //                    existingLocs.add(nm);
    //                }
    //            }
    //        }
    //        
    //        Vector<Locale> locales = new Vector<Locale>();
    //        for (Locale l : Locale.getAvailableLocales())
    //        {
    //            if (!hideExisting || !existingLocs.contains(getFullLang(l)))
    //            {
    //                locales.add(l);
    //            }
    //        }
    //        Collections.sort(locales, new Comparator<Locale>()
    //        {
    //            public int compare(Locale o1, Locale o2)
    //            {
    //                return o1.getDisplayName().compareTo(o2.getDisplayName());
    //            }
    //        });
    //
    //        Vector<String> localeNames = new Vector<String>();
    //        for (Locale l : locales)
    //        {
    //            localeNames.add(l.getDisplayName());
    //        }
    //
    //        ToggleButtonChooserDlg<String> chooser = new ToggleButtonChooserDlg<String>((Frame) null,
    //                "CHOOSE_LOCALE", localeNames, ToggleButtonChooserPanel.Type.RadioButton);
    //        chooser.setUseScrollPane(true);
    //        chooser.setVisible(true);
    //        if (!chooser.isCancelled()) 
    //        { 
    //            return locales.get(chooser.getSelectedIndex()); 
    //        }
    //        return null;
    //    }

    /**
     * Creates an array of POST method parameters to send with the version checking / usage tracking connection.
     * 
     * @param doSendSecondaryStats if true, the POST parameters include usage stats
     * @return an array of POST parameters
     */
    protected NameValuePair[] createPostParameters(final String inputStr, final String src) {
        Vector<NameValuePair> postParams = new Vector<NameValuePair>();
        try {
            // get the install ID
            // get the OS name and version
            postParams.add(new NameValuePair("doit", "done")); //$NON-NLS-1$
            postParams.add(new NameValuePair("ei", "UTF-8")); //$NON-NLS-1$
            postParams.add(new NameValuePair("lp", src)); //$NON-NLS-1$
            postParams.add(new NameValuePair("fr", "bf-home")); //$NON-NLS-1$

            postParams.add(new NameValuePair("intl", "1")); //$NON-NLS-1$
            postParams.add(new NameValuePair("tt", "urltext")); //$NON-NLS-1$
            postParams.add(new NameValuePair("trtext", inputStr)); //$NON-NLS-1$
            //postParams.add(new NameValuePair("wl_trglang",   dst)); //$NON-NLS-1$

            // create an array from the params
            NameValuePair[] paramArray = new NameValuePair[postParams.size()];
            for (int i = 0; i < paramArray.length; ++i) {
                paramArray[i] = postParams.get(i);
            }

            return paramArray;

        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    /* (non-Javadoc)
     * @see java.awt.event.WindowListener#windowActivated(java.awt.event.WindowEvent)
     */
    @Override
    public void windowActivated(WindowEvent arg0) {
        // ignore

    }

    /* (non-Javadoc)
     * @see java.awt.event.WindowListener#windowClosed(java.awt.event.WindowEvent)
     */
    @Override
    public void windowClosed(WindowEvent arg0) {
        // ignore

    }

    /* (non-Javadoc)
     * @see java.awt.event.WindowListener#windowClosing(java.awt.event.WindowEvent)
     */
    @Override
    public void windowClosing(WindowEvent arg0) {
        doExit(checkForChanges());
    }

    /* (non-Javadoc)
     * @see java.awt.event.WindowListener#windowDeactivated(java.awt.event.WindowEvent)
     */
    @Override
    public void windowDeactivated(WindowEvent arg0) {
        // ignore

    }

    /* (non-Javadoc)
     * @see java.awt.event.WindowListener#windowDeiconified(java.awt.event.WindowEvent)
     */
    @Override
    public void windowDeiconified(WindowEvent arg0) {
        // ignore

    }

    /* (non-Javadoc)
     * @see java.awt.event.WindowListener#windowIconified(java.awt.event.WindowEvent)
     */
    @Override
    public void windowIconified(WindowEvent arg0) {
        // ignore

    }

    /* (non-Javadoc)
     * @see java.awt.event.WindowListener#windowOpened(java.awt.event.WindowEvent)
     */
    @Override
    public void windowOpened(WindowEvent arg0) {
        // ignore

    }

    /**
      * @param args
      */
    public static void main(String[] args) {
        setAppName("Specify"); //$NON-NLS-1$
        System.setProperty(AppPreferences.factoryName, "edu.ku.brc.specify.config.AppPrefsDBIOIImpl"); // Needed by AppReferences //$NON-NLS-1$

        for (String s : args) {
            String[] pairs = s.split("="); //$NON-NLS-1$
            if (pairs.length == 2) {
                if (pairs[0].startsWith("-D")) //$NON-NLS-1$
                {
                    //System.err.println("["+pairs[0].substring(2, pairs[0].length())+"]["+pairs[1]+"]");
                    System.setProperty(pairs[0].substring(2, pairs[0].length()), pairs[1]);
                }
            } else {
                String symbol = pairs[0].substring(2, pairs[0].length());
                //System.err.println("["+symbol+"]");
                System.setProperty(symbol, symbol);
            }
        }

        // Now check the System Properties
        String appDir = System.getProperty("appdir");
        if (StringUtils.isNotEmpty(appDir)) {
            UIRegistry.setDefaultWorkingPath(appDir);
        }

        String appdatadir = System.getProperty("appdatadir");
        if (StringUtils.isNotEmpty(appdatadir)) {
            UIRegistry.setBaseAppDataDir(appdatadir);
        }

        // Then set this
        IconManager.setApplicationClass(Specify.class);
        IconManager.loadIcons(XMLHelper.getConfigDir("icons.xml")); //$NON-NLS-1$
        //ImageIcon icon = IconManager.getIcon("AppIcon", IconManager.IconSize.Std16);

        try {
            ResourceBundle.getBundle("resources", Locale.getDefault()); //$NON-NLS-1$

        } catch (MissingResourceException ex) {
            Locale.setDefault(Locale.ENGLISH);
            UIRegistry.setResourceLocale(Locale.ENGLISH);
        }

        try {
            if (!System.getProperty("os.name").equals("Mac OS X")) {
                UIManager.setLookAndFeel(new Plastic3DLookAndFeel());
                PlasticLookAndFeel.setPlasticTheme(new DesertBlue());
            }
        } catch (Exception e) {
            //whatever
        }
        AppPreferences localPrefs = AppPreferences.getLocalPrefs();
        localPrefs.setDirPath(UIRegistry.getAppDataDir());

        boolean doIt = false;
        if (doIt)

        {
            Charset utf8charset = Charset.forName("UTF-8");
            Charset iso88591charset = Charset.forName("ISO-8859-1");

            ByteBuffer inputBuffer = ByteBuffer.wrap(new byte[] { (byte) 0xC3, (byte) 0xA2 });

            // decode UTF-8
            CharBuffer data = utf8charset.decode(inputBuffer);

            // encode ISO-8559-1
            ByteBuffer outputBuffer = iso88591charset.encode(data);
            byte[] outputData = outputBuffer.array();
            System.out.println(new String(outputData));
            return;
        }

        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIHelper.OSTYPE osType = UIHelper.getOSType();
                    if (osType == UIHelper.OSTYPE.Windows) {
                        UIManager.setLookAndFeel(new PlasticLookAndFeel());
                        PlasticLookAndFeel.setPlasticTheme(new ExperienceBlue());

                    } else if (osType == UIHelper.OSTYPE.Linux) {
                        UIManager.setLookAndFeel(new PlasticLookAndFeel());
                    }
                } catch (Exception e) {
                    log.error("Can't change L&F: ", e); //$NON-NLS-1$
                }

                JFrame frame = new JFrame(getResourceString("StrLocalizerApp.AppTitle"));
                frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
                final StrLocalizerAppForiOS sl = new StrLocalizerAppForiOS();
                sl.addMenuBar(frame);
                frame.setContentPane(sl);
                frame.setSize(768, 1024);

                //Dimension size = frame.getPreferredSize();
                //size.height = 500;
                //frame.setSize(size);
                frame.addWindowListener(sl);
                IconManager.setApplicationClass(Specify.class);
                frame.setIconImage(IconManager.getImage(IconManager.makeIconName("SpecifyWhite32")).getImage());

                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        sl.startUp();
                    }
                });
            }
        });
    }

    //-------------------------------------------------------------------------------
    //-- List Model
    //-------------------------------------------------------------------------------
    class ItemModel extends AbstractListModel {
        protected L10NFile l10nFile;

        public ItemModel(final L10NFile l10nFile) {
            this.l10nFile = l10nFile;
        }

        /* (non-Javadoc)
         * @see javax.swing.ListModel#getElementAt(int)
         */
        @Override
        public Object getElementAt(final int index) {
            return l10nFile != null ? l10nFile.getItems().get(index) : "N/A";
        }

        /* (non-Javadoc)
         * @see javax.swing.ListModel#getSize()
         */
        @Override
        public int getSize() {
            return l10nFile != null ? l10nFile.getItems().size() : 0;
        }
    }

    //-------------------------------------------------------------------------------
    //-- L10NItem
    //-------------------------------------------------------------------------------
    private class L10NItem {
        private boolean isUIComp;
        private String title;
        private String comment;
        private String key;
        private String value;
        private boolean isInUse;
        private boolean isChanged;
        private boolean isDuplicate;
        private Boolean isMappable;
        private L10NFile l10nFile;
        private Vector<L10NItem> dups = new Vector<L10NItem>();

        /**
         * @param l10nFile
         * @param comment
         * @param key
         * @param value
         * @param isUIComp
         * @param isInUse
         */
        public L10NItem(final L10NFile l10nFile, final String comment, final String key, final String value,
                final boolean isUIComp, final boolean isInUse) {
            this.l10nFile = l10nFile;
            this.comment = comment;
            this.key = key;
            this.value = value;
            this.isUIComp = isUIComp;
            this.isInUse = isInUse;
            this.isChanged = false;
            this.title = null;
            this.isMappable = null;
            this.isDuplicate = false;
        }

        /**
         * @return the value
         */
        public String getValue() {
            return value;
        }

        /**
         * @param value the value to set
         */
        public void setValue(String value) {
            this.value = value;
            for (L10NItem itm : dups) {
                itm.setValue(value);
            }
        }

        /**
         * @return the isUIComp
         */
        public boolean isUIComp() {
            return isUIComp;
        }

        /**
         * @return the comment
         */
        public String getComment() {
            return comment;
        }

        /**
         * @return the isDuplicate
         */
        public boolean isDuplicate() {
            return isDuplicate;
        }

        /**
         * @param isDuplicate the isDuplicate to set
         */
        public void setDuplicate(boolean isDuplicate) {
            this.isDuplicate = isDuplicate;
        }

        public void addDup(final L10NItem item) {
            if (!isUIComp) {
                mergeComment(item.getComment());
            }
            dups.add(item);
        }

        public int getDupCnt() {
            return dups.size();
        }

        /**
         * @param isChanged the isChanged to set
         */
        public void setChanged(boolean isChanged) {
            this.isChanged = isChanged;
            this.l10nFile.setChanged(true);
        }

        /**
         * @param l10nFile the l10nFile to set
         */
        public void setL10nFile(L10NFile l10nFile) {
            this.l10nFile = l10nFile;
        }

        public void mergeComment(final String newComment) {
            comment = StringUtils.remove(comment, "*/");
            String newCom = StringUtils.remove(newComment, "/*").trim();
            comment += "\n   " + newCom;
        }

        /**
         * @return the isMappable
         */
        public boolean isMappable() {
            if (isMappable == null) {
                String token = isUIComp ? value : key;
                if (!isInUse || isDuplicate || StringUtils.isEmpty(token) || StringUtils.isWhitespace(token)
                        || token.equals("Label") || token.equals("Untitled")
                        || (token.length() == 2 && token.charAt(0) == '%')) {
                    return isMappable = false;
                }
                isMappable = true;
            }
            return isMappable;
        }

        /**
         * @param pw
         */
        public void print(final PrintWriter pw) {
            if (isUIComp || !isDuplicate) {
                if (isInUse)
                    pw.println();
                pw.println(comment);
                pw.println(String.format("\"%s\" = \"%s\";", key, value));
                if (!isInUse)
                    pw.println();
            }
        }

        /**
         * @param attr
         * @return
         */
        private String getAttr(final String attr) {
            if (StringUtils.isNotEmpty(comment)) {
                int inx = comment.indexOf(attr);
                if (inx > 0) {
                    inx += attr.length() + 3;
                    if (comment.charAt(inx) == '"') {
                        inx++;
                        int endInx = comment.indexOf('"', inx);
                        if (endInx > -1) {
                            return comment.substring(inx, endInx);
                        }
                    }
                }
            }
            return null;
        }

        /**
         * @return the title
         */
        public String getTitle() {
            if (isUIComp) {
                if (title == null) {
                    String[] keys = { "title", "text", "headerTitle", "normalTitle", "placeholder", "ObjectID" };
                    for (String k : keys) {
                        title = getAttr(k);
                        if (title != null) {
                            return title;
                        }
                    }
                    title = "N/A";
                }
                return title;
            }
            return key;
        }

        /* (non-Javadoc)
         * @see java.lang.Object#toString()
         */
        @Override
        public String toString() {
            return getTitle();
        }
    }

    //-------------------------------------------------------------------------------
    //-- L10NFile
    //-------------------------------------------------------------------------------
    class L10NFile implements Comparable<L10NFile> {
        private File file;
        private Vector<L10NItem> fileItems;
        private Vector<L10NItem> items;
        private int[] indexMap;
        private boolean isChanged;
        private boolean isUIComp;

        private int currLineInx;
        private HashMap<String, L10NItem> itemsHash = new HashMap<String, L10NItem>();

        L10NUIIndexer uiIndexer = null;

        /**
         * @param file
         */
        public L10NFile(final File file) {
            super();
            this.file = file;
            this.fileItems = null;
            this.isChanged = false;
            this.isUIComp = false;

            String fName = file.getAbsolutePath();
            String sbName = String.format("%s%s.%s", getFullPath(fName), getBaseName(fName), "storyboard");
            File uiFile = new File(sbName);
            if (uiFile.exists()) {
                isUIComp = true;
                uiIndexer = new L10NUIIndexer(rootDir);
                uiIndexer.load(uiFile);
            }
        }

        /**
         * @param s
         * @return
         */
        private String unicodeEscape(String s) {
            try {
                byte[] bytes = new byte[s.length() * 2];
                int i = 0;
                for (byte b : s.getBytes()) {
                    bytes[i++] = b;
                    bytes[i++] = 0;
                }
                return new String(bytes, 0, bytes.length, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            return null;
        }

        /**
         * @param s
         */
        private void dump(String s) {
            try {
                for (byte b : s.getBytes("UTF-8")) {
                    System.out.print(b + ", ");
                }
                System.out.println();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }

        /**
         * @param lines
         * @return
         */
        private String getComment(final List<String> lines) {
            StringBuilder sb = new StringBuilder();
            String line = lines.get(currLineInx);
            int sInx = line.indexOf("/*");
            if (sInx > -1 && sInx < 5) {
                line = line.substring(sInx, line.length());
                do {
                    sb.append(line);
                    currLineInx++;
                    int eInx = line.lastIndexOf("*/");
                    int len = line.length();
                    if (eInx == len - 2) {
                        return sb.toString();
                    }

                    line = lines.get(currLineInx);
                } while (currLineInx < lines.size());
            }
            return null;
        }

        /**
         * @return
         */
        public boolean load() {
            if (file != null && !file.isDirectory() && file.exists()) {
                try {
                    fileItems = new Vector<L10NItem>();

                    List<String> fileLines = (List<String>) FileUtils.readLines(file, "UTF-16");
                    Vector<String> lines = new Vector<String>();
                    for (String line : fileLines) {
                        if (StringUtils.isNotEmpty(line)) {
                            lines.add(line);
                        }
                    }
                    currLineInx = 0;

                    String idStr = null;
                    String initialComment = getComment(lines);
                    if (initialComment != null) {
                        int inx = initialComment.indexOf("ObjectID");
                        if (inx > -1) {
                            isUIComp = true;
                            inx = initialComment.indexOf('"', inx);
                            if (inx > -1) {
                                inx++;
                                int eInx = initialComment.indexOf('"', inx);
                                if (eInx > -1) {
                                    isUIComp = true;
                                    idStr = initialComment.substring(inx, eInx);
                                }
                            }
                        }
                    }

                    boolean hasChanges = false;
                    currLineInx = 0;
                    while (currLineInx < lines.size()) {
                        String comment = getComment(lines);
                        if (comment == null)
                            return false;
                        String line = lines.get(currLineInx++);
                        int inx = line.indexOf("\" = \"");
                        if (inx > -1) {
                            String key = line.substring(1, inx);
                            boolean isInUse = (isUIComp && (uiIndexer == null || uiIndexer.hasId(idStr)))
                                    || (!isUIComp && (srcIndexer == null || srcIndexer.searchFor(key)));
                            String value = line.substring(inx + 5, line.length() - 2);

                            L10NItem item = new L10NItem(this, comment, key, value, isUIComp, isInUse);
                            fileItems.add(item);

                            String itmTitle = item.getTitle();
                            L10NItem itm = itemsHash.get(itmTitle);
                            if (itm == null) {
                                itemsHash.put(itmTitle, item);
                            } else {
                                item.setDuplicate(true);
                                itm.addDup(item);
                            }

                            if (!isInUse) {
                                hasChanges = true;
                                //System.err.println(String.format("Skipping [%s][%s] %s", key, idStr != null ? idStr : "", file.getName()));
                            }
                        } else {
                            return false;
                        }
                    }

                    System.out.println(file.getName() + " Duplicates: " + itemsHash.size());
                    for (L10NItem itm : itemsHash.values()) {
                        if (itm.getDupCnt() > 0)
                            System.out.println("  dup: [" + itm.getTitle() + "] " + itm.getDupCnt());
                    }

                    if (hasChanges) {
                        this.isChanged = true;
                        setDataHasChanged();
                    }

                    items = new Vector<L10NItem>();
                    indexMap = new int[fileItems.size()];

                    // Map localizable items
                    int inx = 0;
                    for (int i = 0; i < fileItems.size(); i++) {
                        L10NItem item = fileItems.get(i);
                        if (item.isMappable()) {
                            indexMap[inx++] = i;
                            items.add(item);
                        }
                    }

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

        /**
         * @return the file
         */
        public File getFile() {
            return file;
        }

        /**
         * @param file the file to set
         */
        public void setFile(File file) {
            this.file = file;
        }

        /**
         * @return the fileItems
         */
        public Vector<L10NItem> getFileItems() {
            return fileItems;
        }

        /**
         * @return the fileItems
         */
        public Vector<L10NItem> getItems() {
            return items;
        }

        /**
         * @param fileItems the fileItems to set
         */
        public void setFileItems(Vector<L10NItem> fileItems) {
            this.fileItems = fileItems;
        }

        /**
         * @param isChanged the isChanged to set
         */
        public void setChanged(boolean isChanged) {
            this.isChanged = isChanged;
        }

        /**
         * @return the isChanged
         */
        public boolean isChanged() {
            return isChanged;
        }

        /**
         * 
         */
        public boolean save() {
            if (this.isChanged) {
                OutputStreamWriter osw;
                try {
                    String fName = file.getAbsolutePath();
                    String ds = (new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss"))
                            .format(Calendar.getInstance().getTime());
                    String bakName = String.format("%s%s_%s.%s.bak", getFullPath(fName), getBaseName(fName), ds,
                            getExtension(fName));
                    FileUtils.copyFile(file, new File(bakName));

                    osw = new OutputStreamWriter(new FileOutputStream(file), "UTF-16");
                    PrintWriter pw = new PrintWriter(osw);
                    for (L10NItem item : fileItems) {
                        item.print(pw);
                    }
                    pw.close();

                    // Do it after saving
                    this.isChanged = false;
                    for (L10NItem item : items) {
                        item.setChanged(false);
                    }
                    return true;

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

        /* (non-Javadoc)
         * @see java.lang.Comparable#compareTo(java.lang.Object)
         */
        @Override
        public int compareTo(final L10NFile o) {
            return file.getName().compareTo(o.getFile().getName());
        }

        /* (non-Javadoc)
         * @see java.lang.Object#toString()
         */
        @Override
        public String toString() {
            return file.getName();
        }

    }
}