Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoralexei.trebounskikh2018-07-20 00:02:20 +0000
committeralexei.trebounskikh2018-08-01 18:01:21 +0000
commitf422548545a931cd4ab2d7d3ba9068607828d902 (patch)
tree5f08ea47d1cb251f45ede54d34666678bf49baf2 /org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/data/TaskDataFileManager.java
parent724035d03db2865890766125a6bc7931cfacbf21 (diff)
downloadorg.eclipse.mylyn.tasks-f422548545a931cd4ab2d7d3ba9068607828d902.tar.gz
org.eclipse.mylyn.tasks-f422548545a931cd4ab2d7d3ba9068607828d902.tar.xz
org.eclipse.mylyn.tasks-f422548545a931cd4ab2d7d3ba9068607828d902.zip
537208: Task data filename can get too long
* moved file-related operations to a separate class * file name is only encoded if required * file name is trimmed to stay below 255 characters * encoded file name always used if file already exists * added unit tests for file-related methods Change-Id: I8b536b3a3df8168b997aa9be1ec82b9f7e314dfd Task-Url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=537208 Signed-off-by: alexei.trebounskikh <alexei.trebounskikh@tasktop.com>
Diffstat (limited to 'org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/data/TaskDataFileManager.java')
-rw-r--r--org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/data/TaskDataFileManager.java128
1 files changed, 128 insertions, 0 deletions
diff --git a/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/data/TaskDataFileManager.java b/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/data/TaskDataFileManager.java
new file mode 100644
index 000000000..3b8fcc7ef
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/data/TaskDataFileManager.java
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Tasktop Technologies and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.tasks.core.data;
+
+import java.io.File;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.function.Predicate;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.mylyn.commons.core.CoreUtil;
+import org.eclipse.mylyn.tasks.core.ITask;
+
+/**
+ * Encapsulates file-related operations of TaskDataManager
+ */
+public class TaskDataFileManager {
+
+ private static final String ENCODING_UTF_8 = "UTF-8"; //$NON-NLS-1$
+
+ private static final String EXTENSION = ".zip"; //$NON-NLS-1$
+
+ private static final String FOLDER_TASKS = "tasks"; //$NON-NLS-1$
+
+ private static final String FOLDER_DATA = "offline"; //$NON-NLS-1$
+
+ private static final String FOLDER_TASKS_1_0 = "offline"; //$NON-NLS-1$
+
+ private static final int FILENAME_MAX_LEN = 255 - EXTENSION.length(); // 255 is an OS limit for file name
+
+ private String dataPath;
+
+ public String getDataPath() {
+ return dataPath;
+ }
+
+ public void setDataPath(String dataPath) {
+ this.dataPath = dataPath;
+ }
+
+ public File getFile(ITask task, String kind) {
+ return getFile(task.getRepositoryUrl(), task, kind);
+ }
+
+ public File getFile(String repositoryUrl, ITask task, String kind) {
+ File path = getDirectory(repositoryUrl, task);
+ String fileName = getFileName(task, path);
+ return new File(path, fileName + EXTENSION);
+ }
+
+ private File getDirectory(String repositoryUrl, ITask task) {
+ Assert.isNotNull(dataPath);
+ String repositoryPath = task.getConnectorKind() + "-" + CoreUtil.asFileName(repositoryUrl); //$NON-NLS-1$
+ return new File(dataPath + File.separator + FOLDER_TASKS + File.separator + repositoryPath + File.separator
+ + FOLDER_DATA);
+ }
+
+ private String getFileName(ITask task, File path) {
+ return getFileName(task, filename -> new File(path, filename + EXTENSION).exists());
+ }
+
+ // the method is made protected for unit testing
+ protected String getFileName(ITask task, Predicate<String> fileExists) {
+ String encodedFileName = CoreUtil.asFileName(task.getTaskId());
+
+ // for backwards-compatibility with versions that always encoded file names,
+ // we will use an encoded name if the file with an encoded name already exists
+ if (fileExists.test(encodedFileName)) {
+ return encodedFileName;
+ }
+
+ // if file with encoded name does not exist, we will only encode file name if it is required
+ String fileName;
+ if (requiresEncoding(task.getTaskId())) {
+ fileName = encodedFileName;
+ } else {
+ fileName = task.getTaskId();
+ }
+
+ // trim the file name if it is too long
+ return trimFilenameIfRequired(fileName);
+ }
+
+ /**
+ * Checks if input contains characters other than ones returned by {@link CoreUtil.asFileName}
+ *
+ * @param fileName
+ * @return true or false
+ */
+ private boolean requiresEncoding(String fileName) {
+ return !fileName.matches("^[a-zA-Z0-9%\\.]+$"); //$NON-NLS-1$
+ }
+
+ private String trimFilenameIfRequired(String filename) {
+ if (filename.length() > FILENAME_MAX_LEN) {
+ // replace a long file name with a shorter name + the hash
+ String hashCode = getHashCode(filename);
+ return filename.substring(0, FILENAME_MAX_LEN - hashCode.length() - 1) + "." + hashCode; //$NON-NLS-1$
+ }
+
+ return filename;
+ }
+
+ private String getHashCode(String text) {
+ return Integer.toUnsignedString(text.hashCode());
+ }
+
+ public File getFile10(ITask task, String kind) {
+ try {
+ String pathName = URLEncoder.encode(task.getRepositoryUrl(), ENCODING_UTF_8);
+ String fileName = task.getTaskId() + EXTENSION;
+ File path = new File(dataPath + File.separator + FOLDER_TASKS_1_0, pathName);
+ return new File(path, fileName);
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+}

Back to the top