/************************************************************************** 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 2012 Guido Leenders, Didier Briel 2013 Aaron Madlon-Kay, Yu Tang 2014 Aaron Madlon-Kay, Alex Buloichik 2015 Aaron Madlon-Kay 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.core.data; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.omegat.core.segmentation.SRX; import org.omegat.filters2.master.FilterMaster; import org.omegat.filters2.master.PluginUtils; import org.omegat.util.FileUtil; import org.omegat.util.Language; import org.omegat.util.Log; import org.omegat.util.OConsts; import org.omegat.util.OStrings; import org.omegat.util.Platform; import org.omegat.util.StringUtil; import gen.core.filters.Filters; import gen.core.project.RepositoryDefinition; /** * Storage for project properties. May read and write project from/to disk. * * @author Keith Godfrey * @author Maxym Mykhalchuk * @author Guido Leenders * @author Didier Briel * @author Aaron Madlon-Kay * @author Yu Tang * @author Alex Buloichik (alex73mail@gmail.com) */ public class ProjectProperties { private static final String[] DEFAULT_EXCLUDES = { "**/.svn/**", "**/CVS/**", "**/.cvs/**", "**/desktop.ini", "**/Thumbs.db", "**/.DS_Store", }; public static List<String> getDefaultExcludes() { return Collections.unmodifiableList(Arrays.asList(DEFAULT_EXCLUDES)); } /** * Constructor for tests only. */ protected ProjectProperties() { } /** * Default constructor to initialize fields (to get no NPEs). Real values * should be applied after creation. */ public ProjectProperties(File projectDir) throws Exception { projectRootDir = projectDir; projectName = projectDir.getName(); setSourceRoot(getProjectRoot() + OConsts.DEFAULT_SOURCE + File.separator); sourceRootExcludes.addAll(Arrays.asList(DEFAULT_EXCLUDES)); setTargetRoot(getProjectRoot() + OConsts.DEFAULT_TARGET + File.separator); setGlossaryRoot(getProjectRoot() + OConsts.DEFAULT_GLOSSARY + File.separator); setWriteableGlossary(getProjectRoot() + OConsts.DEFAULT_GLOSSARY + File.separator + OConsts.DEFAULT_W_GLOSSARY); setTMRoot(getProjectRoot() + OConsts.DEFAULT_TM + File.separator); setDictRoot(getProjectRoot() + OConsts.DEFAULT_DICT + File.separator); setSentenceSegmentingEnabled(true); setSupportDefaultTranslations(true); setRemoveTags(false); setSourceLanguage("EN-US"); setTargetLanguage("EN-GB"); projectSRX = SRX.loadSRX(new File(getProjectInternal(), SRX.CONF_SENTSEG)); projectFilters = FilterMaster.loadConfig(new File(getProjectInternal(), FilterMaster.FILE_FILTERS)); setSourceTokenizer(PluginUtils.getTokenizerClassForLanguage(getSourceLanguage())); setTargetTokenizer(PluginUtils.getTokenizerClassForLanguage(getTargetLanguage())); } /** Returns The Target (Compiled) Files Directory */ public String getTargetRoot() { return targetDir.getAsString(); } /** Sets The Target (Compiled) Files Directory */ public void setTargetRoot(String targetRoot) { targetDir.setRelativeOrAbsolute(targetRoot); } public ProjectPath getTargetDir() { return targetDir; } /** Returns The Glossary Files Directory */ public String getGlossaryRoot() { return glossaryDir.getAsString(); } /** Sets The Glossary Files Directory */ public void setGlossaryRoot(String glossaryRoot) { glossaryDir.setRelativeOrAbsolute(glossaryRoot); } public ProjectPath getGlossaryDir() { return glossaryDir; } public ProjectPath getWritableGlossaryFile() { return writableGlossaryFile; } /** Returns The Glossary File Location */ public String getWriteableGlossary() { return writableGlossaryFile.getAsString(); } /** Returns The Glossary File Directory */ public String getWriteableGlossaryDir() { ProjectPath dir = new ProjectPath(true); dir.setRelativeOrAbsolute(writableGlossaryFile.getAsFile().getParent()); return dir.getAsString(); } /** Sets The Writeable Glossary File Location */ public void setWriteableGlossary(String writeableGlossaryFile) { writableGlossaryFile.setRelativeOrAbsolute(writeableGlossaryFile); } public boolean isDefaultWriteableGlossaryFile() { return computeDefaultWriteableGlossaryFile().equals(writableGlossaryFile.getAsString()); } public String computeDefaultWriteableGlossaryFile() { // Default glossary file name depends on where glossaryDir is: // - Inside project folder: glossary.txt // - Outside project folder: ${projectName}-glossary.txt String glossaryDir = getGlossaryRoot(); if (glossaryDir.startsWith(getProjectRoot())) { return glossaryDir + OConsts.DEFAULT_W_GLOSSARY; } else { return glossaryDir + projectName + OConsts.DEFAULT_W_GLOSSARY_SUFF; } } public ProjectPath getTmDir() { return tmDir; } /** Returns The Translation Memory (TMX) Files Directory */ public String getTMRoot() { return tmDir.getAsString(); } /** Sets The Translation Memory (TMX) Files Directory */ public void setTMRoot(String tmRoot) { tmDir.setRelativeOrAbsolute(tmRoot); } /** Returns The Translation Memory (TMX) with translations to other languages Files Directory */ public String getTMOtherLangRoot() { return tmDir.getAsString() + OConsts.DEFAULT_OTHERLANG + '/'; } /** Returns The Translation Memory (TMX) Files Directory for automatically applied files. */ public String getTMAutoRoot() { return tmDir.getAsString() + OConsts.AUTO_TM + '/'; } public ProjectPath getDictDir() { return dictDir; } /** Returns The Dictionaries Files Directory */ public String getDictRoot() { return dictDir.getAsString(); } /** Sets Dictionaries Files Directory */ public void setDictRoot(String dictRoot) { dictDir.setRelativeOrAbsolute(dictRoot); } public String getDictRootRelative() { return dictDir.getAsString(); } /** Returns the name of the Project */ public String getProjectName() { return projectName; } /** Returns The Project Root Directory */ public String getProjectRoot() { String p = projectRootDir.getPath().replace('\\', '/'); if (!p.endsWith("/")) { p += '/'; } return p; } public File getProjectRootDir() { return projectRootDir; } /** Sets The Project Root Directory. For unit tests only !!! */ protected void setProjectRoot(String projectRoot) { this.projectRootDir = new File(projectRoot); } /** Returns The Project's Translation Memory (TMX) File */ public String getProjectInternal() { return getProjectRoot() + OConsts.DEFAULT_INTERNAL + '/'; } public File getProjectInternalDir() { return new File(projectRootDir, OConsts.DEFAULT_INTERNAL); } public String getProjectInternalRelative() { return OConsts.DEFAULT_INTERNAL + '/'; } /** Returns The Source (to be translated) Files Directory */ public String getSourceRoot() { return sourceDir.getAsString(); } /** Sets The Source (to be translated) Files Directory */ public void setSourceRoot(String sourceRoot) { if (!StringUtil.isEmpty(sourceRoot)) { sourceDir.setRelativeOrAbsolute(sourceRoot); } } public void setSourceRootRelative(String sourceRootRelative) { if (!StringUtil.isEmpty(sourceRootRelative)) { sourceDir.setRelativeOrAbsolute(sourceRootRelative); } } public ProjectPath getSourceDir() { return sourceDir; } public List<String> getSourceRootExcludes() { return sourceRootExcludes; } /** * Returns The Source Language (language of the source files) of the Project */ public Language getSourceLanguage() { return sourceLanguage; } /** Sets The Source Language (language of the source files) of the Project */ public void setSourceLanguage(Language sourceLanguage) { this.sourceLanguage = sourceLanguage; } /** Sets The Source Language (language of the source files) of the Project */ public void setSourceLanguage(String sourceLanguage) { this.sourceLanguage = new Language(sourceLanguage); } /** * Returns The Target Language (language of the translated files) of the Project */ public Language getTargetLanguage() { return targetLanguage; } /** * Sets The Target Language (language of the translated files) of the Project */ public void setTargetLanguage(Language targetLanguage) { this.targetLanguage = targetLanguage; } /** * Sets The Target Language (language of the translated files) of the Project */ public void setTargetLanguage(String targetLanguage) { this.targetLanguage = new Language(targetLanguage); } /** * Returns the class name of the source language tokenizer for the Project. */ public Class<?> getSourceTokenizer() { if (sourceTokenizer == null) { Class<?> cls = PluginUtils.getTokenizerClassForLanguage(getSourceLanguage()); setSourceTokenizer(cls); } return sourceTokenizer; } /** * Sets the class name of the source language tokenizer for the Project. */ public void setSourceTokenizer(Class<?> sourceTokenizer) { this.sourceTokenizer = sourceTokenizer; } /** * Returns the class name of the target language tokenizer for the Project. */ public Class<?> getTargetTokenizer() { return targetTokenizer; } /** * Sets the class name of the target language tokenizer for the Project. */ public void setTargetTokenizer(Class<?> targetTokenizer) { this.targetTokenizer = targetTokenizer; } /** * Returns whether The Sentence Segmenting is Enabled for this Project. Default, Yes. */ public boolean isSentenceSegmentingEnabled() { return sentenceSegmentingEnabled; } /** Sets whether The Sentence Segmenting is Enabled for this Project */ public void setSentenceSegmentingEnabled(boolean sentenceSegmentingEnabled) { this.sentenceSegmentingEnabled = sentenceSegmentingEnabled; } public boolean isSupportDefaultTranslations() { return supportDefaultTranslations; } public void setSupportDefaultTranslations(boolean supportDefaultTranslations) { this.supportDefaultTranslations = supportDefaultTranslations; } public boolean isRemoveTags() { return removeTags; } public void setRemoveTags(boolean removeTags) { this.removeTags = removeTags; } public boolean hasRepositories() { return repositories != null && !repositories.isEmpty(); } public List<RepositoryDefinition> getRepositories() { return repositories; } public void setRepositories(List<RepositoryDefinition> repositories) { this.repositories = repositories; } public SRX getProjectSRX() { return projectSRX; } public void setProjectSRX(SRX projectSRX) { this.projectSRX = projectSRX; } public Filters getProjectFilters() { return projectFilters; } public void setProjectFilters(Filters projectFilters) { this.projectFilters = projectFilters; } public String getExternalCommand() { return externalCommand; } public void setExternalCommand(String command) { this.externalCommand = command; } public boolean isProjectValid() { boolean returnValue; try { verifyProject(); returnValue = true; } catch (ProjectException ex) { returnValue = false; } return returnValue; } /** * Verify project and print any problems. */ public void verifyProject() throws ProjectException { // // Now check whether these directories are where they're suposed to be. // String srcDir = getSourceRoot(); File src = new File(srcDir); if (!src.exists()) { throw new ProjectException(StringUtil.format(OStrings.getString("PROJECT_SOURCE_FOLDER"), srcDir)); } // String tgtDir = getTargetRoot(); File tgt = new File(tgtDir); if (!tgt.exists()) { throw new ProjectException(StringUtil.format(OStrings.getString("PROJECT_TARGET_FOLDER"), tgtDir)); } // String glsDir = getGlossaryRoot(); File gls = new File(glsDir); if (!gls.exists()) { throw new ProjectException(StringUtil.format(OStrings.getString("PROJECT_GLOSSARY_FOLDER"), glsDir)); } String wGlsDir = getWriteableGlossaryDir(); if (!wGlsDir.contains(getGlossaryRoot())) { throw new ProjectException(StringUtil.format(OStrings.getString("PROJECT_W_GLOSSARY"), glsDir)); } // String tmxDir = getTMRoot(); File tmx = new File(tmxDir); if (!tmx.exists()) { throw new ProjectException(StringUtil.format(OStrings.getString("PROJECT_TM_FOLDER"), tmxDir)); } // Dictionary folder is always created automatically when it does not exist, for ascending // compatibility reasons. // There is no exception handling when a failure occurs during folder creation. // File dict = new File(getDictRoot()); if (!dict.exists()) { if (getDictRoot().equals(getProjectRoot() + OConsts.DEFAULT_DICT + '/')) { dict.mkdirs(); } } } public void autocreateDirectories() { autocreateOneDirectory(getProjectInternalDir()); autocreateOneDirectory(sourceDir.getAsFile()); autocreateOneDirectory(targetDir.getAsFile()); autocreateOneDirectory(glossaryDir.getAsFile()); autocreateOneDirectory(tmDir.getAsFile()); autocreateOneDirectory(dictDir.getAsFile()); } private void autocreateOneDirectory(File dir) { if (!dir.exists()) { Log.logInfoRB("CT_AUTOCREATE_DIRECTORY", dir); dir.mkdirs(); } } private String projectName; private final List<String> sourceRootExcludes = new ArrayList<String>(); private List<RepositoryDefinition> repositories; private Language sourceLanguage; private Language targetLanguage; private Class<?> sourceTokenizer; private Class<?> targetTokenizer; private boolean sentenceSegmentingEnabled; private boolean supportDefaultTranslations; private boolean removeTags; private SRX projectSRX; private Filters projectFilters; private String externalCommand; protected File projectRootDir; protected ProjectPath sourceDir = new ProjectPath(true); protected ProjectPath targetDir = new ProjectPath(true); protected ProjectPath glossaryDir = new ProjectPath(true); protected ProjectPath writableGlossaryFile = new ProjectPath(false); protected ProjectPath tmDir = new ProjectPath(true); protected ProjectPath dictDir = new ProjectPath(true); /** * Class for support project path functionality, like relative path, etc. */ public final class ProjectPath { private final boolean isDirectory; private File fs; /** Null if path is not under project root */ private String underRoot; /** * @param isDirectory * true if directory(i.e. should be ended by '/'), false if file */ public ProjectPath(boolean isDirectory) { this.isDirectory = isDirectory; } /** * path is directory(or file) as declared in the omegat.project, but not __DEFAULT__. I.e. caller can * send something like "/some/project/source", or "source", or "source/". * * Absolute paths from Windows will be treated as relative on Linux/MacOS, and vice versa. */ public void setRelativeOrAbsolute(String path) { underRoot = null; if (FileUtil.isRelative(path)) { Path p = projectRootDir == null ? Paths.get(path) : projectRootDir.toPath().resolve(path); fs = p.normalize().toFile(); if (!path.contains("..")) { underRoot = path.replace('\\', '/'); if (isDirectory && !underRoot.endsWith("/")) { underRoot += '/'; } } } else { fs = new File(FileUtil.absoluteForSystem(path, Platform.getOsType())); // probably relative ? try { String p = FileUtil.computeRelativePath(projectRootDir, fs); if (!p.contains("..")) { underRoot = p.replace('\\', '/'); if (isDirectory && !underRoot.endsWith("/")) { underRoot += '/'; } } } catch (IOException ex) { // absolute } } } public File getAsFile() { return fs; } public String getAsString() { String p = fs.getPath().replace('\\', '/'); if (isDirectory && !p.endsWith("/")) { p += '/'; } return p; } public boolean isUnderRoot() { return underRoot != null; } /** * Returns path under project root with '/' at the end for directories, or null if directory outside * of project. */ public String getUnderRoot() { return underRoot; } } }