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