/************************************************************************** OmegaT - Computer Assisted Translation (CAT) tool with fuzzy matching, translation memory, keyword search, glossaries, and translation leveraging into updated projects. Copyright (C) 2000-2006 Keith Godfrey and Maxym Mykhalchuk 2009 Martin Fleurke, Alex Buloichik, Didier Briel 2012 Aaron Madlon-Kay 2013 Kyle Katarn, Aaron Madlon-Kay 2014 Alex Buloichik Home page: http://www.omegat.org/ Support center: http://groups.yahoo.com/group/OmegaT/ This file is part of OmegaT. OmegaT 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 3 of the License, or (at your option) any later version. OmegaT 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, see <http://www.gnu.org/licenses/>. **************************************************************************/ package org.omegat; import java.awt.Toolkit; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.lang.reflect.Field; import java.nio.charset.Charset; import java.text.MessageFormat; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.PropertyResourceBundle; import java.util.TreeMap; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import javax.swing.UIManager; import org.omegat.CLIParameters.PSEUDO_TRANSLATE_TYPE; import org.omegat.CLIParameters.TAG_VALIDATION_MODE; import org.omegat.convert.ConvertConfigs; import org.omegat.core.Core; import org.omegat.core.CoreEvents; import org.omegat.core.data.NotLoadedProject; import org.omegat.core.data.PrepareTMXEntry; import org.omegat.core.data.ProjectProperties; import org.omegat.core.data.RealProject; import org.omegat.core.data.SourceTextEntry; import org.omegat.core.data.TMXEntry; import org.omegat.core.events.IProjectEventListener; import org.omegat.core.tagvalidation.ErrorReport; import org.omegat.core.team2.TeamTool; import org.omegat.filters2.master.FilterMaster; import org.omegat.filters2.master.PluginUtils; import org.omegat.gui.main.ProjectUICommands; import org.omegat.gui.scripting.ScriptItem; import org.omegat.gui.scripting.ScriptRunner; import org.omegat.util.Log; import org.omegat.util.OConsts; import org.omegat.util.OStrings; import org.omegat.util.Platform; import org.omegat.util.Preferences; import org.omegat.util.ProjectFileStorage; import org.omegat.util.RuntimePreferences; import org.omegat.util.StringUtil; import org.omegat.util.TMXWriter; import org.omegat.util.gui.OSXIntegration; import com.vlsolutions.swing.docking.DockingDesktop; /** * The main OmegaT class, used to launch the program. * * @author Keith Godfrey * @author Martin Fleurke * @author Alex Buloichik * @author Didier Briel * @author Aaron Madlon-Kay * @author Kyle Katarn */ public final class Main { private Main() { } /** Project location for load on startup. */ protected static File projectLocation = null; /** Remote project location. */ protected static String remoteProject = null; /** Execution command line parameters. */ protected static final Map<String, String> PARAMS = new TreeMap<>(); /** Execution mode. */ protected static CLIParameters.RUN_MODE runMode = CLIParameters.RUN_MODE.GUI; public static void main(String[] args) { if (args.length > 0 && (CLIParameters.HELP_SHORT.equals(args[0]) || CLIParameters.HELP.equals(args[0]))) { System.out.println(StringUtil.format(OStrings.getString("COMMAND_LINE_HELP"), OStrings.getNameAndVersion())); System.exit(0); } if (args.length > 0 && CLIParameters.TEAM_TOOL.equals(args[0])) { TeamTool.main(Arrays.copyOfRange(args, 1, args.length)); } // Workaround for bug #812. Remove this when appropriate; see // https://sourceforge.net/p/omegat/bugs/812/ System.setProperty("jna.encoding", Charset.defaultCharset().name()); PARAMS.putAll(CLIParameters.parseArgs(args)); String projectDir = PARAMS.get(CLIParameters.PROJECT_DIR); if (projectDir != null) { projectLocation = new File(projectDir); } remoteProject = PARAMS.get(CLIParameters.REMOTE_PROJECT); applyConfigFile(PARAMS.get(CLIParameters.CONFIG_FILE)); runMode = CLIParameters.RUN_MODE.parse(PARAMS.get(CLIParameters.MODE)); String resourceBundle = PARAMS.get(CLIParameters.RESOURCE_BUNDLE); if (resourceBundle != null) { OStrings.loadBundle(resourceBundle); } String configDir = PARAMS.get(CLIParameters.CONFIG_DIR); if (configDir != null) { RuntimePreferences.setConfigDir(configDir); } if (PARAMS.containsKey(CLIParameters.QUIET)) { RuntimePreferences.setQuietMode(true); } if (PARAMS.containsKey(CLIParameters.DISABLE_PROJECT_LOCKING)) { RuntimePreferences.setProjectLockingEnabled(false); } if (PARAMS.containsKey(CLIParameters.DISABLE_LOCATION_SAVE)) { RuntimePreferences.setLocationSaveEnabled(false); } Log.log("\n" + "===================================================================" + "\n" + OStrings.getNameAndVersion() + " (" + new Date() + ") " + " Locale " + Locale.getDefault()); Log.logRB("LOG_STARTUP_INFO", System.getProperty("java.vendor"), System.getProperty("java.version"), System.getProperty("java.home")); System.setProperty("http.user", OStrings.getDisplayNameAndVersion()); // Do migration and load various settings. The order is important! ConvertConfigs.convert(); Preferences.init(); PluginUtils.loadPlugins(PARAMS); FilterMaster.setFilterClasses(PluginUtils.getFilterClasses()); Preferences.initFilters(); Preferences.initSegmentation(); int result; try { switch (runMode) { case GUI: result = runGUI(); // GUI has own shutdown code break; case CONSOLE_TRANSLATE: result = runConsoleTranslate(); PluginUtils.unloadPlugins(); break; case CONSOLE_CREATEPSEUDOTRANSLATETMX: result = runCreatePseudoTranslateTMX(); PluginUtils.unloadPlugins(); break; case CONSOLE_ALIGN: result = runConsoleAlign(); PluginUtils.unloadPlugins(); break; default: result = 1; } } catch (Throwable ex) { Log.log(ex); showError(ex); result = 1; } if (result != 0) { System.exit(result); } } /** * Load System properties from a specified .properties file. In order to * allow this to reliably change the display language, it must called before * any use of {@link Log#log}, thus it logs to {@link System#out}. * * @param path * to config file */ private static void applyConfigFile(String path) { if (path == null) { return; } File configFile = new File(path); if (!configFile.exists()) { return; } System.out.println("Reading config from " + path); try (FileInputStream in = new FileInputStream(configFile)) { PropertyResourceBundle config = new PropertyResourceBundle(in); // Put config properties into System properties and into OmegaT params. for (String key : config.keySet()) { String value = config.getString(key); System.setProperty(key, value); PARAMS.put(key, value); System.out.println("Read from config: " + key + "=" + value); } // Apply language preferences, if present. // This must be done with Locale.setDefault(). Merely doing // System.setProperty() will not work. if (config.containsKey("user.language")) { String userLanguage = config.getString("user.language"); Locale userLocale = config.containsKey("user.country") ? new Locale(userLanguage, config.getString("user.country")) : new Locale(userLanguage); Locale.setDefault(userLocale); } } catch (FileNotFoundException exception) { System.err.println("Config file not found: " + path); } catch (IOException exception) { System.err.println("Error while reading config file: " + path); } } /** * Execute standard GUI. */ protected static int runGUI() { // MacOSX-specific - they must be setted BEFORE any GUI calls if (Platform.isMacOSX()) { OSXIntegration.init(); } Log.log("Docking Framework version: " + DockingDesktop.getDockingFrameworkVersion()); Log.log(""); // Set X11 application class name to make some desktop user interfaces // (like Gnome Shell) recognize OmegaT Toolkit toolkit = Toolkit.getDefaultToolkit(); Class<?> cls = toolkit.getClass(); try { if (cls.getName().equals("sun.awt.X11.XToolkit")) { Field field = cls.getDeclaredField("awtAppClassName"); field.setAccessible(true); field.set(toolkit, "OmegaT"); } } catch (Exception e) { // do nothing } try { // Workaround for JDK bug 6389282 (OmegaT bug bug 1555809) // it should be called before setLookAndFeel() for GTK LookandFeel // Contributed by Masaki Katakai (SF: katakai) UIManager.getInstalledLookAndFeels(); UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); System.setProperty("swing.aatext", "true"); } catch (Exception e) { // do nothing Log.logErrorRB("MAIN_ERROR_CANT_INIT_OSLF"); } try { Core.initializeGUI(PARAMS); } catch (Throwable ex) { Log.log(ex); showError(ex); return 1; } if (!Core.getPluginsLoadingErrors().isEmpty()) { String err = String.join("\n", Core.getPluginsLoadingErrors()); JOptionPane.showMessageDialog(JOptionPane.getRootFrame(), err, OStrings.getString("STARTUP_ERRORBOX_TITLE"), JOptionPane.ERROR_MESSAGE); } CoreEvents.fireApplicationStartup(); SwingUtilities.invokeLater(new Runnable() { public void run() { // setVisible can't be executed directly, because we need to // call all application startup listeners for initialize UI Core.getMainWindow().getApplicationFrame().setVisible(true); if (remoteProject != null) { ProjectUICommands.projectRemote(remoteProject); } else if (projectLocation != null) { ProjectUICommands.projectOpen(projectLocation); } } }); return 0; } /** * Execute in console mode for translate. */ protected static int runConsoleTranslate() throws Exception { Log.log("Console translation mode"); Log.log(""); System.out.println(OStrings.getString("CONSOLE_INITIALIZING")); Core.initializeConsole(PARAMS); RealProject p = selectProjectConsoleMode(true); validateTagsConsoleMode(); System.out.println(OStrings.getString("CONSOLE_TRANSLATING")); String sourceMask = PARAMS.get(CLIParameters.SOURCE_PATTERN); if (sourceMask != null) { p.compileProject(sourceMask, false); } else { p.compileProject(".*", false); } // Called *after* executing post processing command (unlike the // regular PROJECT_CHANGE_TYPE.COMPILE) executeConsoleScript(IProjectEventListener.PROJECT_CHANGE_TYPE.COMPILE); p.closeProject(); executeConsoleScript(IProjectEventListener.PROJECT_CHANGE_TYPE.CLOSE); System.out.println(OStrings.getString("CONSOLE_FINISHED")); return 0; } /** * Validates tags according to command line specs: * <code>--tag-validation=[abort|warn]</code> * <p> * On abort, the program is aborted when tag validation finds errors. On * warn the errors are printed but the program continues. In all other cases * no tag validation is done. */ private static void validateTagsConsoleMode() { TAG_VALIDATION_MODE mode = TAG_VALIDATION_MODE.parse(PARAMS.get(CLIParameters.TAG_VALIDATION)); List<ErrorReport> stes; switch (mode) { case ABORT: System.out.println(OStrings.getString("CONSOLE_VALIDATING_TAGS")); stes = Core.getTagValidation().listInvalidTags(); if (!stes.isEmpty()) { Core.getTagValidation().logTagValidationErrors(stes); System.out.println(OStrings.getString("CONSOLE_TAGVALIDATION_FAIL")); System.out.println(OStrings.getString("CONSOLE_TAGVALIDATION_ABORT")); System.exit(1); } break; case WARN: System.out.println(OStrings.getString("CONSOLE_VALIDATING_TAGS")); stes = Core.getTagValidation().listInvalidTags(); if (!stes.isEmpty()) { Core.getTagValidation().logTagValidationErrors(stes); System.out.println(OStrings.getString("CONSOLE_TAGVALIDATION_FAIL")); } break; default: //do not validate tags = default } } /** * Execute in console mode for translate. */ protected static int runCreatePseudoTranslateTMX() throws Exception { Log.log("Console pseudo-translate mode"); Log.log(""); System.out.println(OStrings.getString("CONSOLE_INITIALIZING")); Core.initializeConsole(PARAMS); RealProject p = selectProjectConsoleMode(true); validateTagsConsoleMode(); System.out.println(OStrings.getString("CONSOLE_CREATE_PSEUDOTMX")); ProjectProperties config = p.getProjectProperties(); List<SourceTextEntry> entries = p.getAllEntries(); String pseudoTranslateTMXFilename = PARAMS.get(CLIParameters.PSEUDOTRANSLATETMX); PSEUDO_TRANSLATE_TYPE pseudoTranslateType = PSEUDO_TRANSLATE_TYPE .parse(PARAMS.get(CLIParameters.PSEUDOTRANSLATETYPE)); String fname; if (!StringUtil.isEmpty(pseudoTranslateTMXFilename)) { if (!pseudoTranslateTMXFilename.endsWith(OConsts.TMX_EXTENSION)) { fname = pseudoTranslateTMXFilename + "." + OConsts.TMX_EXTENSION; } else { fname = pseudoTranslateTMXFilename; } } else { fname = ""; } // prepare tmx Map<String, PrepareTMXEntry> data = new HashMap<>(); for (SourceTextEntry ste : entries) { PrepareTMXEntry entry = new PrepareTMXEntry(); entry.source = ste.getSrcText(); switch (pseudoTranslateType) { case EQUAL: entry.translation = ste.getSrcText(); break; case EMPTY: entry.translation = ""; break; } data.put(ste.getSrcText(), entry); } try { // Write OmegaT-project-compatible TMX: TMXWriter.buildTMXFile(fname, false, false, config, data); } catch (IOException e) { Log.logErrorRB("CT_ERROR_CREATING_TMX"); Log.log(e); throw new IOException(OStrings.getString("CT_ERROR_CREATING_TMX") + "\n" + e.getMessage()); } p.closeProject(); System.out.println(OStrings.getString("CONSOLE_FINISHED")); return 0; } public static int runConsoleAlign() throws Exception { Log.log("Console alignment mode"); Log.log(""); if (projectLocation == null) { System.out.println(OStrings.getString("PP_ERROR_UNABLE_TO_READ_PROJECT_FILE")); return 1; } String dir = PARAMS.get(CLIParameters.ALIGNDIR); if (dir == null) { System.out.println(OStrings.getString("CONSOLE_TRANSLATED_FILES_LOC_UNDEFINED")); return 1; } System.out.println(OStrings.getString("CONSOLE_INITIALIZING")); Core.initializeConsole(PARAMS); RealProject p = selectProjectConsoleMode(true); validateTagsConsoleMode(); System.out.println(StringUtil.format(OStrings.getString("CONSOLE_ALIGN_AGAINST"), dir)); Map<String, TMXEntry> data = p.align(p.getProjectProperties(), new File(dir)); Map<String, PrepareTMXEntry> result = new TreeMap<>(); for (Map.Entry<String, TMXEntry> en : data.entrySet()) { result.put(en.getKey(), new PrepareTMXEntry(en.getValue())); } String tmxFile = p.getProjectProperties().getProjectInternal() + "align.tmx"; TMXWriter.buildTMXFile(tmxFile, false, false, p.getProjectProperties(), result); p.closeProject(); System.out.println(OStrings.getString("CONSOLE_FINISHED")); return 0; } /** * creates the project class and adds it to the Core. Loads the project if * specified. An exit occurs on error loading the project. This method is * for the different console modes, to prevent code duplication. * * @param loadProject * load the project or not * @return the project. */ private static RealProject selectProjectConsoleMode(boolean loadProject) { System.out.println(OStrings.getString("CONSOLE_LOADING_PROJECT")); // check if project okay ProjectProperties projectProperties = null; try { projectProperties = ProjectFileStorage.loadProjectProperties(projectLocation); projectProperties.verifyProject(); } catch (Exception ex) { Log.logErrorRB(ex, "PP_ERROR_UNABLE_TO_READ_PROJECT_FILE"); System.out.println(OStrings.getString("PP_ERROR_UNABLE_TO_READ_PROJECT_FILE")); System.exit(1); } RealProject p = new RealProject(projectProperties); Core.setProject(p); if (loadProject) { p.loadProject(true); if (!p.isProjectLoaded()) { Core.setProject(new NotLoadedProject()); } else { executeConsoleScript(IProjectEventListener.PROJECT_CHANGE_TYPE.LOAD); } } return p; } /** Execute script as PROJECT_CHANGE events. We can't use the regular project listener because * the SwingUtilities.invokeLater method used in CoreEvents doesn't stop the project processing * in console mode. */ private static void executeConsoleScript(IProjectEventListener.PROJECT_CHANGE_TYPE eventType) { if (PARAMS.containsKey(CLIParameters.SCRIPT)) { File script = new File(PARAMS.get("script").toString()); if (script.isFile()) { HashMap<String, Object> binding = new HashMap<>(); binding.put("eventType", eventType); try { String result = ScriptRunner.executeScript(new ScriptItem(script), binding); Log.log(result); } catch (Exception ex) { Log.log(ex); } } } } public static void showError(Throwable ex) { String msg; if (StringUtil.isEmpty(ex.getMessage())) { msg = ex.getClass().getName(); } else { msg = ex.getMessage(); } switch (runMode) { case GUI: JOptionPane.showMessageDialog(JOptionPane.getRootFrame(), msg, OStrings.getString("STARTUP_ERRORBOX_TITLE"), JOptionPane.ERROR_MESSAGE); break; default: System.err.println(MessageFormat.format(OStrings.getString("CONSOLE_ERROR"), msg)); break; } } }