diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskUtil.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskUtil.java index a94f9e8dc7bc..b29f1e35d61f 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskUtil.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskUtil.java @@ -16,9 +16,7 @@ package com.intellij.tasks.impl; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonElement; +import com.google.gson.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.JDOMUtil; @@ -30,11 +28,13 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.InputStream; +import java.lang.reflect.Type; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; import java.util.List; +import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -43,6 +43,10 @@ import java.util.regex.Pattern; */ public class TaskUtil { private static SimpleDateFormat ISO8601_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); + static { + // Use UTC time zone by default (for formatting) + ISO8601_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); + } // Almost ISO-8601 strict except date parts may be separated by '/' // and date only also allowed just in case private static Pattern ISO8601_DATE_PATTERN = Pattern.compile( @@ -50,6 +54,14 @@ public class TaskUtil { "(?:[ T](\\d{2}:\\d{2}:\\d{2})(.\\d{3,})?" + // optional time and milliseconds "([+-]\\d{2}:\\d{2}|[+-]\\d{4}|[+-]\\d{2}|Z)?)?");// optional timezone info + private static final JsonDeserializer DATE_DESERIALIZER = new JsonDeserializer() { + @Override + public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + return parseDate(json.getAsString()); + } + }; + + public static String formatTask(@NotNull Task task, String format) { return format.replace("{id}", task.getId()).replace("{number}", task.getNumber()) .replace("{project}", task.getProject() == null ? "" : task.getProject()).replace("{summary}", task.getSummary()); @@ -112,8 +124,12 @@ public class TaskUtil { } } + public static String formatDate(@NotNull Date date) { + return ISO8601_DATE_FORMAT.format(date); + } + /** - * {@link Task#equals(Object)} implementation compares tasks by they unique IDs only. + * {@link Task#equals(Object)} implementation compares tasks by their unique IDs only. * This method should be used when full comparison is necessary. */ public static boolean tasksEqual(@NotNull Task t1, @NotNull Task t2) { @@ -195,4 +211,8 @@ public class TaskUtil { logger.debug("\n" + gson.toJson(gson.fromJson(json, JsonElement.class))); } } + + public static GsonBuilder installDateDeserializer(GsonBuilder builder) { + return builder.registerTypeAdapter(Date.class, DATE_DESERIALIZER); + } } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/JiraRepository.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/JiraRepository.java index 4270254efd4a..d5eda2e9facc 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/JiraRepository.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/JiraRepository.java @@ -1,5 +1,7 @@ package com.intellij.tasks.jira; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Comparing; @@ -8,6 +10,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.tasks.Task; import com.intellij.tasks.TaskState; import com.intellij.tasks.impl.BaseRepositoryImpl; +import com.intellij.tasks.impl.TaskUtil; import com.intellij.tasks.jira.model.JiraIssue; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; @@ -28,6 +31,7 @@ import java.util.List; @Tag("JIRA") public class JiraRepository extends BaseRepositoryImpl { + public static final Gson GSON = TaskUtil.installDateDeserializer(new GsonBuilder()).create(); private final static Logger LOG = Logger.getInstance("#com.intellij.tasks.jira.JiraRepository"); public static final String LOGIN_FAILED_CHECK_YOUR_PERMISSIONS = "Login failed. Check your permissions."; public static final String REST_API_PATH_SUFFIX = "/rest/api/latest"; @@ -141,7 +145,7 @@ public class JiraRepository extends BaseRepositoryImpl { LOG.warn("Can't find out JIRA REST API version"); throw e; } - JsonObject object = JiraUtil.GSON.fromJson(responseBody, JsonObject.class); + JsonObject object = GSON.fromJson(responseBody, JsonObject.class); // when JIRA 4.x support will be dropped 'versionNumber' array in response // may be used instead version string parsing return JiraRestApi.fromJiraVersion(object.get("version").getAsString(), this); @@ -177,7 +181,7 @@ public class JiraRepository extends BaseRepositoryImpl { else if (method.getResponseHeader("Content-Type") != null) { Header header = method.getResponseHeader("Content-Type"); if (header.getValue().startsWith("application/json")) { - JsonObject object = JiraUtil.GSON.fromJson(entityContent, JsonObject.class); + JsonObject object = GSON.fromJson(entityContent, JsonObject.class); if (object.has("errorMessages")) { String reason = StringUtil.join(object.getAsJsonArray("errorMessages"), " "); // something meaningful to user, e.g. invalid field name in JQL query diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/JiraUtil.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/JiraUtil.java deleted file mode 100644 index af300e0f792e..000000000000 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/JiraUtil.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2000-2013 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.tasks.jira; - -import com.google.gson.*; -import com.intellij.tasks.impl.TaskUtil; - -import java.lang.reflect.Type; -import java.util.Date; - -/** - * @author Mikhail Golubev - */ -public class JiraUtil { - public static final Gson GSON = buildGson(); - - private static Gson buildGson() { - GsonBuilder gson = new GsonBuilder(); - gson.registerTypeAdapter(Date.class, new DateDeserializer()); - return gson.create(); - } - - private static class DateDeserializer implements JsonDeserializer { - @Override - public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - return TaskUtil.parseDate(json.getAsString()); - } - } -} diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/model/api2/JiraRestApi2.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/model/api2/JiraRestApi2.java index a8a88eae9b89..e362dc263291 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/model/api2/JiraRestApi2.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/model/api2/JiraRestApi2.java @@ -5,7 +5,6 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.tasks.TaskState; import com.intellij.tasks.jira.JiraRepository; import com.intellij.tasks.jira.JiraRestApi; -import com.intellij.tasks.jira.JiraUtil; import com.intellij.tasks.jira.model.JiraIssue; import com.intellij.tasks.jira.model.JiraResponseWrapper; import org.apache.commons.httpclient.methods.GetMethod; @@ -41,7 +40,7 @@ public class JiraRestApi2 extends JiraRestApi { @NotNull @Override protected List parseIssues(String response) { - JiraResponseWrapper.Issues wrapper = JiraUtil.GSON.fromJson(response, ISSUES_WRAPPER_TYPE); + JiraResponseWrapper.Issues wrapper = JiraRepository.GSON.fromJson(response, ISSUES_WRAPPER_TYPE); return new ArrayList(wrapper.getIssues()); } @@ -57,7 +56,7 @@ public class JiraRestApi2 extends JiraRestApi { @Nullable @Override protected JiraIssue parseIssue(String response) { - return JiraUtil.GSON.fromJson(response, JiraIssueApi2.class); + return JiraRepository.GSON.fromJson(response, JiraIssueApi2.class); } @Nullable diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/model/api20alpha1/JiraRestApi20Alpha1.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/model/api20alpha1/JiraRestApi20Alpha1.java index df0ff53f0c0e..eda4430ffd21 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/model/api20alpha1/JiraRestApi20Alpha1.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/model/api20alpha1/JiraRestApi20Alpha1.java @@ -5,7 +5,6 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.tasks.TaskState; import com.intellij.tasks.jira.JiraRepository; import com.intellij.tasks.jira.JiraRestApi; -import com.intellij.tasks.jira.JiraUtil; import com.intellij.tasks.jira.model.JiraIssue; import com.intellij.tasks.jira.model.JiraResponseWrapper; import org.jetbrains.annotations.NotNull; @@ -29,13 +28,13 @@ public class JiraRestApi20Alpha1 extends JiraRestApi { @Override protected JiraIssue parseIssue(String response) { - return JiraUtil.GSON.fromJson(response, JiraIssueApi20Alpha1.class); + return JiraRepository.GSON.fromJson(response, JiraIssueApi20Alpha1.class); } @NotNull @Override protected List parseIssues(String response) { - JiraResponseWrapper.Issues wrapper = JiraUtil.GSON.fromJson(response, ISSUES_WRAPPER_TYPE); + JiraResponseWrapper.Issues wrapper = JiraRepository.GSON.fromJson(response, ISSUES_WRAPPER_TYPE); List incompleteIssues = wrapper.getIssues(); List updatedIssues = new ArrayList(); for (JiraIssueApi20Alpha1 issue : incompleteIssues) {