blob: 938c61524c3e2a836d5caf0814ac13703b24886c [file] [log] [blame]
package com.intellij.lang.javascript.boilerplate;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.Files;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.platform.templates.github.GeneratorException;
import com.intellij.platform.templates.github.GithubTagInfo;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
/**
* @author Sergey Simonchik
*/
public class GithubTagListProvider {
private static final Logger LOG = Logger.getInstance(GithubTagListProvider.class);
private final String myUserName;
private final String myRepositoryName;
public GithubTagListProvider(@NotNull String userName, @NotNull String repositoryName) {
myUserName = userName;
myRepositoryName = repositoryName;
}
@Nullable
public ImmutableSet<GithubTagInfo> getCachedTags() {
ApplicationManager.getApplication().assertIsDispatchThread();
File cacheFile = getTagsCacheFile();
if (cacheFile.isFile()) {
try {
ImmutableSet<GithubTagInfo> tags = readTagsFromFile(cacheFile);
LOG.info(getGeneratorName() + "tag info list has been successfully read from cache file " + cacheFile.getAbsolutePath());
return tags;
} catch (GeneratorException e) {
LOG.warn("Can't read cache file " + cacheFile.getAbsolutePath(), e);
}
}
return null;
}
public void updateTagListAsynchronously(@NotNull final GithubProjectGeneratorPeer peer) {
Runnable action = createUpdateTagListAction(peer);
if (ApplicationManager.getApplication().isUnitTestMode()) {
action.run();
}
else {
ApplicationManager.getApplication().executeOnPooledThread(action);
}
}
private Runnable createUpdateTagListAction(@NotNull final GithubProjectGeneratorPeer peer) {
return new Runnable() {
@Override
public void run() {
final String[] urls = formatTagListDownloadUrls();
String firstErrorMessage = null;
for (String url : urls) {
String errorMessage;
try {
final ImmutableSet<GithubTagInfo> tags = fetchGithubTagsByUrl(url);
LOG.info(getGeneratorName() + "Cache has been successfully updated");
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
peer.onTagsUpdated(tags);
}
});
return;
}
catch (IOException e) {
errorMessage = "Can not fetch tags from " + url;
LOG.warn(getGeneratorName() + errorMessage, e);
}
catch (GeneratorException e) {
errorMessage = "Malformed JSON received from " + url;
LOG.warn(getGeneratorName() + errorMessage, e);
}
if (firstErrorMessage == null) {
firstErrorMessage = errorMessage;
}
}
if (firstErrorMessage != null) {
peer.onTagsUpdateError(firstErrorMessage);
}
}
};
}
private ImmutableSet<GithubTagInfo> fetchGithubTagsByUrl(@NotNull final String url) throws IOException, GeneratorException {
LOG.info(getGeneratorName() + "starting cache update from " + url + " ...");
File cacheFile = getTagsCacheFile();
GithubDownloadUtil.downloadAtomically(null, url, cacheFile, myUserName, myRepositoryName);
return readTagsFromFile(cacheFile);
}
private String getGeneratorName() {
return "[" + myUserName + "/" + myRepositoryName + "] ";
}
@NotNull
private ImmutableSet<GithubTagInfo> readTagsFromFile(@NotNull File file) throws GeneratorException {
final String content;
try {
content = Files.toString(file, Charsets.UTF_8);
}
catch (IOException e) {
throw new GeneratorException("Can not read '" + file.getAbsolutePath() + "'!", e);
}
try {
return parseContent(content);
} catch (GeneratorException e) {
String message = String.format("%s parsing version list failed: %s\n%s",
getGeneratorName(),
e.getMessage(),
content);
LOG.info(message, e);
throw e;
}
}
@NotNull
private static ImmutableSet<GithubTagInfo> parseContent(@NotNull String tagFileContent) throws GeneratorException {
if (tagFileContent.trim().isEmpty()) {
throw new GeneratorException("Can not parse fetched version list: got empty response");
}
final JsonElement jsonElement;
try {
JsonParser jsonParser = new JsonParser();
jsonElement = jsonParser.parse(tagFileContent);
} catch (Exception e) {
throw new GeneratorException("Can not parse fetched version list: malformed JSON was received");
}
return toGithubTagList(jsonElement);
}
@NotNull
private static ImmutableSet<GithubTagInfo> toGithubTagList(@NotNull JsonElement jsonElement) throws GeneratorException {
if (jsonElement instanceof JsonArray) {
JsonArray array = (JsonArray) jsonElement;
ImmutableSet.Builder<GithubTagInfo> tags = ImmutableSet.builder();
for (JsonElement element : array) {
if (element instanceof JsonObject) {
JsonObject obj = (JsonObject) element;
JsonElement nameElement = obj.get("name");
String name = null;
if (nameElement != null) {
name = nameElement.getAsString();
}
String zipball = null;
JsonElement zipballElement = obj.get("zipball_url");
if (zipballElement != null) {
zipball = zipballElement.getAsString();
}
if (name != null && zipball != null) {
tags.add(new GithubTagInfo(name, zipball));
}
}
else {
throw new GeneratorException("Unexpected child element " + element.getClass().getName());
}
}
return tags.build();
}
else {
throw new GeneratorException("jsonElement is expected be instance of " + JsonArray.class.getName());
}
}
@NotNull
private File getTagsCacheFile() {
File dir = GithubDownloadUtil.getCacheDir(myUserName, myRepositoryName);
return new File(dir, "tags.json");
}
@NotNull
private String[] formatTagListDownloadUrls() {
return new String[] {
"https://api.github.com/repos/" + myUserName + "/" + myRepositoryName + "/tags",
"http://download.jetbrains.com/idea/project_templates/github-tags/" + myUserName + "-" + myRepositoryName + "-tags.json"
};
}
}