public enum ResourceType {
	JSON(".json"), 
	ES6(".js"), 
	Properties(".properties");
	
	private final String extension;
	
	/**
	 * Gets the file extension of the resource type.
	 * 
	 * @return 	the file extension.
	 */
	public String getExtension() {
		return extension;
	}
	
	private ResourceType(String extension) {
		this.extension = extension;
	}
} public class AddLocaleMenuItem extends JMenuItem {
	private final static long serialVersionUID = -5108677891532028898L;

	public AddLocaleMenuItem(Editor editor, boolean enabled) {
		super(MessageBundle.get("menu.edit.add.locale.title"));
		setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        addActionListener(e -> editor.showAddLocaleDialog());
        setEnabled(enabled);
	}
} public class AddTranslationMenuItem extends JMenuItem {
	private final static long serialVersionUID = -2673278052970076105L;
	
	public AddTranslationMenuItem(Editor editor, TranslationTreeNode node, boolean enabled) {
		this(editor, enabled, e -> editor.showAddTranslationDialog(node));
	}
	
	public AddTranslationMenuItem(Editor editor, TranslationTree tree, boolean enabled) {
		this(editor, enabled, e -> editor.showAddTranslationDialog(tree.getSelectionNode()));
	}
	
	private AddTranslationMenuItem(Editor editor, boolean enabled, ActionListener action) {
		super(MessageBundle.get("menu.edit.add.translation.title"));
		setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        addActionListener(action);
        setEnabled(enabled);
	}
} public class CollapseTranslationsMenuItem extends JMenuItem {
	private final static long serialVersionUID = 7885728865417192564L;

	public CollapseTranslationsMenuItem(TranslationTree tree) {
        super(MessageBundle.get("menu.view.collapse.title"));
     	addActionListener(e -> tree.collapseAll());
	}
} public class CopyTranslationKeyToClipboardMenuItem extends JMenuItem {
	private static final long serialVersionUID = 6032182493888769724L;

	public CopyTranslationKeyToClipboardMenuItem(Editor editor, boolean enabled) {
        super(MessageBundle.get("menu.edit.copy.key.title"));
        setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
		addActionListener(e -> editor.copySelectedTranslationKey());
		setEnabled(enabled);
	}

}
 public class DuplicateTranslationMenuItem extends JMenuItem {
	private final static long serialVersionUID = 5207946396515235714L;
	
	public DuplicateTranslationMenuItem(Editor editor, boolean enabled) {
        super(MessageBundle.get("menu.edit.duplicate.title"));
		setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
		addActionListener(e -> editor.duplicateSelectedTranslation());
		setEnabled(enabled);
	}
} public class ExpandTranslationsMenuItem extends JMenuItem {
	private final static long serialVersionUID = 7316102121075733726L;

	public ExpandTranslationsMenuItem(TranslationTree tree) {
        super(MessageBundle.get("menu.view.expand.title"));
     	addActionListener(e -> tree.expandAll());
	}
} public class FindTranslationMenuItem extends JMenuItem {
	private final static long serialVersionUID = -1298283182450978961L;

	public FindTranslationMenuItem(Editor editor, boolean enabled) {
		super(MessageBundle.get("menu.edit.find.translation.title"));
		setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        addActionListener(e -> editor.showFindTranslationDialog());
        setEnabled(enabled);
	}
} public class RemoveTranslationMenuItem extends JMenuItem {
	private final static long serialVersionUID = 5207946396515235714L;
	
	public RemoveTranslationMenuItem(Editor editor, boolean enabled) {
        super(MessageBundle.get("menu.edit.delete.title"));
        setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0));
		addActionListener(e -> editor.removeSelectedTranslation());
		setEnabled(enabled);
	}
} public class RenameTranslationMenuItem extends JMenuItem {
	private final static long serialVersionUID = 907122077814626286L;

	public RenameTranslationMenuItem(Editor editor, boolean enabled) {
        super(MessageBundle.get("menu.edit.rename.title"));
        setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
		addActionListener(e -> editor.renameSelectedTranslation());
		setEnabled(enabled);
	}
} public final class Dialogs {
	
	public static void showErrorDialog(Component parent, String title, String message) {
		JOptionPane.showMessageDialog(parent, message, title, JOptionPane.ERROR_MESSAGE);
	}
	
	public static void showWarningDialog(Component parent, String title, String message) {
		JOptionPane.showMessageDialog(parent, message, title, JOptionPane.WARNING_MESSAGE);
	}
	
	public static void showInfoDialog(Component parent, String title, String message) {
		JOptionPane.showMessageDialog(parent, message, title, JOptionPane.INFORMATION_MESSAGE);
	}
	
	public static void showMessageDialog(Component parent, String title, String message) {
		JOptionPane.showMessageDialog(parent, message, title, JOptionPane.PLAIN_MESSAGE);
	}
	
	public static void showComponentDialog(Component parent, String title, Component component) {
		JOptionPane.showMessageDialog(parent, component, title, JOptionPane.PLAIN_MESSAGE);
	}
	
	public static void showHtmlDialog(Component parent, String title, String body) {
		Font font = parent.getFont();
		JHtmlPane pane = new JHtmlPane(parent, "<html><body style=\"font-family:" + 
				font.getFamily() + ";text-align:center;\">" + body + "</body></html>");
		pane.setBorder(BorderFactory.createEmptyBorder(15,15,15,15));
		showComponentDialog(parent, title, pane);	
	}
	
	public static boolean showConfirmDialog(Component parent, String title, String message, int type) {
		return JOptionPane.showConfirmDialog(parent, message, title, JOptionPane.YES_NO_OPTION, type) == JOptionPane.YES_OPTION;
	}
	
	public static String showInputDialog(Component parent, String title, String label, String help, int type) {
		return showInputDialog(parent, title, label, help, type, null, new BlinkCaret());
	}
	
	public static String showInputDialog(Component parent, String title, String label, String help, int type, String initialText, Caret caret) {
		JPanel content = new JPanel(new GridLayout(0, 1));
		
		if (!Strings.isNullOrEmpty(label)) {
			content.add(new JLabel(label));
		}
		
		JTextField field =  new JTextField(initialText);
		field.addAncestorListener(new RequestInitialFocusListener());
		field.setCaret(caret);
		if (initialText != null) {
			field.setCaretPosition(initialText.length());			
		}
		content.add(field);
		
		if (!Strings.isNullOrEmpty(help)) {
			content.add(new JHelpLabel(help));
		}
		
		JPanel container = new JPanel(new GridBagLayout());
		container.add(content);
		
		int result = JOptionPane.showConfirmDialog(parent, container, title, JOptionPane.OK_CANCEL_OPTION, type);
		return result == JOptionPane.OK_OPTION ? field.getText() : null;
	}
} public final class GithubRepoUtil {
	private final static Logger log = LoggerFactory.getLogger(GithubRepoUtil.class);
	private final static Gson gson = new Gson();
	private final static ExecutorService executor;
	
	static {
		executor = Executors.newCachedThreadPool(new ThreadFactoryBuilder()
				.setNameFormat("github-repo-util-pool-%d")
				.build());
	}
	
	/**
	 * Gets the full Github Repository URL.
	 * 
	* @param 	username the Github Repository user name.
	 * @param 	project the Github Repository project name.
	 * @return	the full Github Repository URL.
	 */
	public static String getURL(String username, String project) {
		return "https://github.com/" + username + "/" + project;
	}
	
	/**
	 * Gets the latest release data of a Github Repository.
	 * 
	 * @param 	username the Github Repository user name.
	 * @param 	project the Github Repository project name.
	 * @return	the latest Github Repository release data.
	 */
	public static Future<GithubRepoReleaseData> getLatestRelease(String username, String project) {
		return executor.submit(() -> {
			HttpURLConnection connection = null;
			URL url = new URL("https://api.github.com/repos/" + username + "/" + project + "/releases/latest");
			try {
				connection = (HttpURLConnection)url.openConnection();
				try (InputStreamReader reader = new InputStreamReader(connection.getInputStream(), Charsets.UTF_8)) {
					return gson.fromJson(reader, GithubRepoReleaseData.class);
				}
			} catch (IOException e) {
				log.error("Unable to retrieve latest github release data", e);
				return null;
			} finally {
				if (connection != null) {
					connection.disconnect();
				}
			}
		});
	}
	
	/**
	 * This class represents Github Repository release data.
	 */
	public final class GithubRepoReleaseData {
		@SerializedName("tag_name")
		private String tagName;
		@SerializedName("html_url")
		private String htmlUrl;
		
		public String getTagName() {
			return tagName;
		}
		
		public String getHtmlUrl() {
			return htmlUrl;
		}
	}
} public final class MessageBundle {
	private final static String RESOURCES_PATH = "bundles/messages";
	private static ResourceBundle RESOURCES;
	
	/**
	 * Sets the preferred locale to use.
	 * When calling this function resources will be reloaded from disk.
	 * 
	 * @param 	locale the preferred locale to use
	 */
	public static void setLocale(Locale locale) {
		RESOURCES = ResourceBundle.getBundle(RESOURCES_PATH, locale);
	}
	
	/**
	 * Gets a value from this bundle for the given {@code key}. Any second arguments will
	 * be used to format the value.
	 * 
	 * @param 	key the bundle key
	 * @param 	args objects used to format the value.
	 * @return 	the formatted value for the given key.
	 */
	public static String get(String key, Object... args) {
		String value = RESOURCES.getString(key);
		return MessageFormat.format(value, args);
	}
	
	/**
	 * Gets a mnemonic value from this bundle.
	 * 
	 * @param 	key the bundle key.
	 * @return 	the mnemonic value for the given key.
	 */
	public static Character getMnemonic(String key) {
		String value = RESOURCES.getString(key);
		return value.charAt(0);
	}
	
	/**
	 * Loads the resources.
	 */
	public static void loadResources() {
		RESOURCES = ResourceBundle.getBundle(RESOURCES_PATH);
	}
}
import java.awt.Desktop;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import com.jvms.i18neditor.ResourceType;
import com.jvms.i18neditor.editor.menu.AddLocaleMenuItem;
import com.jvms.i18neditor.editor.menu.AddTranslationMenuItem;
import com.jvms.i18neditor.editor.menu.CollapseTranslationsMenuItem;
import com.jvms.i18neditor.editor.menu.CopyTranslationKeyToClipboardMenuItem;
import com.jvms.i18neditor.editor.menu.DuplicateTranslationMenuItem;
import com.jvms.i18neditor.editor.menu.ExpandTranslationsMenuItem;
import com.jvms.i18neditor.editor.menu.FindTranslationMenuItem;
import com.jvms.i18neditor.editor.menu.RemoveTranslationMenuItem;
import com.jvms.i18neditor.editor.menu.RenameTranslationMenuItem;
import com.jvms.i18neditor.swing.util.Dialogs;
import com.jvms.i18neditor.util.GithubRepoUtil;
import com.jvms.i18neditor.util.MessageBundle;
package com.jvms.i18neditor.editor;




/**
 * This class represents the top bar menu of the editor.
 * 
 * @author Jacob van Mourik
 */
public class EditorMenuBar extends JMenuBar {
	private final static long serialVersionUID = -101788804096708514L;
	
	private final Editor editor;
	private final TranslationTree tree;
	private JMenuItem saveMenuItem;
	private JMenuItem reloadMenuItem;
	private JMenuItem addTranslationMenuItem;
	private JMenuItem findTranslationMenuItem;
	private JMenuItem renameTranslationMenuItem;
	private JMenuItem copyTranslationKeyMenuItem;
	private JMenuItem duplicateTranslationMenuItem;
	private JMenuItem removeTranslationMenuItem;
	private JMenuItem openContainingFolderMenuItem;
	private JMenuItem projectSettingsMenuItem;
	private JMenuItem editorSettingsMenuItem;
	private JMenu openRecentMenuItem;
	private JMenu editMenu;
	private JMenu viewMenu;
	private JMenu settingsMenu;
	
	public EditorMenuBar(Editor editor, TranslationTree tree) {
		super();
		this.editor = editor;
		this.tree = tree;
		setupUI();
		setEnabled(false);
		setSaveable(false);
		setEditable(false);
	}
	
	@Override
	public void setEnabled(boolean enabled) {
		reloadMenuItem.setEnabled(enabled);
		openContainingFolderMenuItem.setEnabled(enabled);
		editMenu.setEnabled(enabled);
		viewMenu.setEnabled(enabled);
		settingsMenu.removeAll();
		if (enabled) {
			settingsMenu.add(projectSettingsMenuItem);
			settingsMenu.addSeparator();
			settingsMenu.add(editorSettingsMenuItem);
		} else {
			settingsMenu.add(editorSettingsMenuItem);
		}
		SwingUtilities.updateComponentTreeUI(this);
	}
	
	public void setSaveable(boolean saveable) {
		saveMenuItem.setEnabled(saveable);
	}
	
	public void setEditable(boolean editable) {
		addTranslationMenuItem.setEnabled(editable);
		findTranslationMenuItem.setEnabled(editable);
	}
	
	public void setRecentItems(List<String> items) {
		openRecentMenuItem.removeAll();
     	if (items.isEmpty()) {
     		openRecentMenuItem.setEnabled(false);
     	} else {
     		openRecentMenuItem.setEnabled(true);
     		for (int i = 0; i < items.size(); i++) {
     			Integer n = i + 1;
     			JMenuItem menuItem = new JMenuItem(n + ": " + items.get(i), Character.forDigit(i, 10));
     			Path path = Paths.get(menuItem.getText().replaceFirst("[0-9]+: ",""));
     			menuItem.addActionListener(e -> editor.importProject(path, true));
     			openRecentMenuItem.add(menuItem);
     		}
     		JMenuItem clearMenuItem = new JMenuItem(MessageBundle.get("menu.file.recent.clear.title"));
     		clearMenuItem.addActionListener(e -> editor.clearHistory());
     		openRecentMenuItem.addSeparator();
     		openRecentMenuItem.add(clearMenuItem);
     	}
	}
	
	private void setupUI() {
		int keyMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
		
		// File menu
     	JMenu fileMenu = new JMenu(MessageBundle.get("menu.file.title"));
     	fileMenu.setMnemonic(MessageBundle.getMnemonic("menu.file.vk"));
        
     	JMenuItem createJsonMenuItem = new JMenuItem(MessageBundle.get("menu.file.project.new.json.title"));
     	createJsonMenuItem.addActionListener(e -> editor.showCreateProjectDialog(ResourceType.JSON));
     	createJsonMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_J, keyMask+KeyEvent.SHIFT_DOWN_MASK));
        
        JMenuItem createEs6MenuItem = new JMenuItem(MessageBundle.get("menu.file.project.new.es6.title"));
        createEs6MenuItem.addActionListener(e -> editor.showCreateProjectDialog(ResourceType.ES6));
        createEs6MenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, keyMask+KeyEvent.SHIFT_DOWN_MASK));
        
        JMenuItem createPropertiesMenuItem = new JMenuItem(MessageBundle.get("menu.file.project.new.properties.title"));
        createPropertiesMenuItem.addActionListener(e -> editor.showCreateProjectDialog(ResourceType.Properties));
        createPropertiesMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, keyMask+KeyEvent.SHIFT_DOWN_MASK));
        
        JMenu createMenuItem = new JMenu(MessageBundle.get("menu.file.project.new.title"));
        createMenuItem.setMnemonic(MessageBundle.getMnemonic("menu.file.project.new.vk"));
        createMenuItem.add(createJsonMenuItem);
        createMenuItem.add(createEs6MenuItem);
        createMenuItem.add(createPropertiesMenuItem);
     	
        JMenuItem importMenuItem = new JMenuItem(MessageBundle.get("menu.file.project.import.title"));
        importMenuItem.setMnemonic(MessageBundle.getMnemonic("menu.file.project.import.vk"));
        importMenuItem.addActionListener(e -> editor.showImportProjectDialog());
        
        openContainingFolderMenuItem = new JMenuItem(MessageBundle.get("menu.file.folder.title"));
        openContainingFolderMenuItem.addActionListener(e -> editor.openProjectDirectory());
        
        openRecentMenuItem = new JMenu(MessageBundle.get("menu.file.recent.title"));
        openRecentMenuItem.setMnemonic(MessageBundle.getMnemonic("menu.file.recent.vk"));
        
        saveMenuItem = new JMenuItem(MessageBundle.get("menu.file.save.title"), MessageBundle.getMnemonic("menu.file.save.vk"));
        saveMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, keyMask));
        saveMenuItem.addActionListener(e -> editor.saveProject());
        
        reloadMenuItem = new JMenuItem(MessageBundle.get("menu.file.reload.title"), MessageBundle.getMnemonic("menu.file.reload.vk"));
        reloadMenuItem.addActionListener(e -> editor.reloadProject());
        
        JMenuItem exitMenuItem = new JMenuItem(MessageBundle.get("menu.file.exit.title"), MessageBundle.getMnemonic("menu.file.exit.vk"));
        exitMenuItem.addActionListener(e -> editor.dispatchEvent(new WindowEvent(editor, WindowEvent.WINDOW_CLOSING)));
        
        fileMenu.add(createMenuItem);
        fileMenu.add(importMenuItem);
        if (Desktop.isDesktopSupported()) {
	        fileMenu.add(openContainingFolderMenuItem);
		}
        fileMenu.add(openRecentMenuItem);
        fileMenu.addSeparator();
        fileMenu.add(saveMenuItem);
        fileMenu.add(reloadMenuItem);
        fileMenu.addSeparator();
        fileMenu.add(exitMenuItem);
        
        // Edit menu
     	editMenu = new JMenu(MessageBundle.get("menu.edit.title"));
     	editMenu.setMnemonic(MessageBundle.getMnemonic("menu.edit.vk"));
     	
        addTranslationMenuItem = new AddTranslationMenuItem(editor, tree, false);
        findTranslationMenuItem = new FindTranslationMenuItem(editor, false);
        removeTranslationMenuItem = new RemoveTranslationMenuItem(editor, false);
        duplicateTranslationMenuItem = new DuplicateTranslationMenuItem(editor, true);