af833b4619ee573e6250ca41c98b720f85fb6e31
[idea/community.git] / java / idea-ui / src / com / intellij / platform / templates / RemoteTemplatesFactory.java
1 // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.platform.templates;
3
4 import com.intellij.ide.plugins.PluginManagerCore;
5 import com.intellij.ide.util.projectWizard.WizardContext;
6 import com.intellij.openapi.application.ApplicationInfo;
7 import com.intellij.openapi.diagnostic.Logger;
8 import com.intellij.openapi.extensions.PluginId;
9 import com.intellij.openapi.module.ModuleType;
10 import com.intellij.openapi.module.ModuleTypeManager;
11 import com.intellij.openapi.util.ClearableLazyValue;
12 import com.intellij.openapi.util.JDOMUtil;
13 import com.intellij.platform.ProjectTemplate;
14 import com.intellij.platform.ProjectTemplatesFactory;
15 import com.intellij.util.ArrayUtilRt;
16 import com.intellij.util.NullableFunction;
17 import com.intellij.util.containers.ContainerUtil;
18 import com.intellij.util.containers.MultiMap;
19 import com.intellij.util.io.HttpRequests;
20 import org.intellij.lang.annotations.Language;
21 import org.jdom.Element;
22 import org.jdom.JDOMException;
23 import org.jetbrains.annotations.NotNull;
24 import org.jetbrains.annotations.Nullable;
25 import org.jetbrains.annotations.TestOnly;
26
27 import java.io.IOException;
28 import java.util.Collection;
29 import java.util.List;
30 import java.util.zip.ZipInputStream;
31
32 /**
33  * @author Dmitry Avdeev
34  */
35 public final class RemoteTemplatesFactory extends ProjectTemplatesFactory {
36   private final static Logger LOG = Logger.getInstance(RemoteTemplatesFactory.class);
37
38   private static final String URL = "https://download.jetbrains.com/idea/project_templates/";
39
40   private final ClearableLazyValue<MultiMap<String, ArchivedProjectTemplate>> myTemplates = ClearableLazyValue.create(() -> {
41     try {
42       return HttpRequests.request(URL + ApplicationInfo.getInstance().getBuild().getProductCode() + "_templates.xml")
43         .connect(request -> {
44           try {
45             return create(JDOMUtil.load(request.getReader()));
46           }
47           catch (JDOMException e) {
48             LOG.error(e);
49             return MultiMap.empty();
50           }
51         });
52     }
53     catch (IOException e) {  // timeouts, lost connection etc
54       LOG.info(e);
55     }
56     catch (Exception e) {
57       LOG.error(e);
58     }
59     return MultiMap.empty();
60   });
61
62   @Override
63   public String @NotNull [] getGroups() {
64     myTemplates.drop();
65     return ArrayUtilRt.toStringArray(myTemplates.getValue().keySet());
66   }
67
68   @Override
69   public ProjectTemplate @NotNull [] createTemplates(@Nullable String group, WizardContext context) {
70     Collection<ArchivedProjectTemplate> templates = myTemplates.getValue().get(group);
71     return templates.isEmpty() ? ProjectTemplate.EMPTY_ARRAY : templates.toArray(ProjectTemplate.EMPTY_ARRAY);
72   }
73
74   @NotNull
75   @TestOnly
76   public static MultiMap<String, ArchivedProjectTemplate> createFromText(@NotNull @Language("XML") String value) throws IOException, JDOMException {
77     return create(JDOMUtil.load(value));
78   }
79
80   @NotNull
81   private static MultiMap<String, ArchivedProjectTemplate> create(@NotNull Element element) {
82     MultiMap<String, ArchivedProjectTemplate> map = MultiMap.create();
83     for (ArchivedProjectTemplate template : createGroupTemplates(element)) {
84       map.putValue(template.getCategory(), template);
85     }
86     return map;
87   }
88
89   private static List<ArchivedProjectTemplate> createGroupTemplates(Element groupElement) {
90     List<Element> children = groupElement.getChildren(ArchivedProjectTemplate.TEMPLATE);
91     return ContainerUtil.mapNotNull(children, (NullableFunction<Element, ArchivedProjectTemplate>)element -> {
92       if (!checkRequiredPlugins(element)) {
93         return null;
94       }
95
96       final ModuleType moduleType = ModuleTypeManager.getInstance().findByID(element.getChildText("moduleType"));
97       final String path = element.getChildText("path");
98       final String description = element.getChildTextTrim("description");
99       String name = element.getChildTextTrim("name");
100       RemoteProjectTemplate template = new RemoteProjectTemplate(name, element, moduleType, path, description);
101       template.populateFromElement(element);
102       return template;
103     });
104   }
105
106   private static boolean checkRequiredPlugins(Element element) {
107     for (Element plugin : element.getChildren("requiredPlugin")) {
108       if (!PluginManagerCore.isPluginInstalled(PluginId.getId(plugin.getTextTrim()))) {
109         return false;
110       }
111     }
112     return true;
113   }
114
115   private static class RemoteProjectTemplate extends ArchivedProjectTemplate {
116     private final ModuleType myModuleType;
117     private final String myPath;
118     private final String myDescription;
119
120     RemoteProjectTemplate(String name,
121                           Element element,
122                           ModuleType moduleType,
123                           String path, String description) {
124       super(name, element.getChildTextTrim("category"));
125       myModuleType = moduleType;
126       myPath = path;
127       myDescription = description;
128     }
129
130     @Override
131     protected ModuleType getModuleType() {
132       return myModuleType;
133     }
134
135     @Override
136     public <T> T processStream(@NotNull StreamProcessor<T> consumer) throws IOException {
137       return HttpRequests.request(URL + myPath).connect(request -> {
138         try (ZipInputStream zip = new ZipInputStream(request.getInputStream())) {
139           return consumer.consume(zip);
140         }
141       });
142     }
143
144     @Nullable
145     @Override
146     public String getDescription() {
147       return myDescription;
148     }
149   }
150 }