From 588427be73ccd786a47866b68e1a88061aa71585 Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Thu, 2 Apr 2015 18:55:37 +0300 Subject: [PATCH] Configurable refactoring: build tree of configurable (tested) --- .../ex/ConfigurableExtensionPointUtil.java | 142 +++++++++++++++++- 1 file changed, 135 insertions(+), 7 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableExtensionPointUtil.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableExtensionPointUtil.java index 06637c48383d..54e153aaad8b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableExtensionPointUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableExtensionPointUtil.java @@ -132,35 +132,130 @@ public class ConfigurableExtensionPointUtil { } /** - * @param project the project used to load application settings + * @param configurables a list of settings to process + * @return the map of different groups of settings + */ + public static Map> groupConfigurables(@NotNull List configurables) { + Map> tree = ContainerUtil.newHashMap(); + for (Configurable configurable : configurables) { + if (configurable instanceof ConfigurableWrapper) { + ConfigurableWrapper wrapper = (ConfigurableWrapper)configurable; + String id = wrapper.getId(); + Node node = Node.get(tree, id); + if (node.myValue != null) { + LOG.warn("ignore configurable with duplicated id: " + id); + } + else { + String parentId = wrapper.getParentId(); + String groupId = wrapper.getExtensionPoint().groupId; + if (groupId != null) { + if (parentId != null) { + LOG.warn("ignore deprecated groupId: " + groupId + " for id: " + id); + } + else { + LOG.warn("use deprecated groupId instead of parentId: " + groupId + " for id: " + id); + parentId = groupId; + } + } + if (Node.cyclic(tree, parentId, node)) { + LOG.warn("ignore cyclic dependency: " + parentId + " cannot contain " + id); + parentId = null; + } + node.myParent = Node.add(tree, parentId, node); + node.myValue = wrapper; + } + } + else { + Node.add(tree, null, configurable); + } + } + Map> map = ContainerUtil.newHashMap(); + for (String id : tree.keySet().toArray(new String[tree.size()])) { + Node node = tree.get(id); + if (node != null) { + List list = getConfigurables(tree, node); + if (list != null) { + map.put(id, list); + tree.remove(id); + } + } + } + return map; + } + + /** + * @param tree a map that represents a tree of nodes + * @param node a current node to process children recursively + * @return the list of settings for a group or {@code null} for internal node + */ + private static List getConfigurables(Map> tree, Node node) { + List list = ContainerUtil.newArrayListWithCapacity(node.myChildren.size()); + for (Iterator iterator = node.myChildren.iterator(); iterator.hasNext(); iterator.remove()) { + Object child = iterator.next(); + if (child instanceof Configurable) { + list.add((Configurable)child); + } + else { + @SuppressWarnings("unchecked") // expected type + Node value = (Node)child; + if (getConfigurables(tree, value) != null) { + throw new IllegalStateException("unexpected algorithm state"); + } + list.add(value.myValue); + tree.remove(value.myValue.getId()); + } + } + if (node.myValue == null) { + return list; // for group only + } + for (Configurable configurable : list) { + node.myValue = node.myValue.addChild(configurable); + } + return null; + } + + /** + * @param project a project used to load project settings or {@code null} * @param withIdeSettings specifies whether to load application settings or not * @param loadComponents specifies whether to load Configurable components or not - * @return the list of all available settings according to parameters + * @return the list of all valid settings according to parameters */ - private static List getAllConfigurables(@Nullable Project project, boolean withIdeSettings, boolean loadComponents) { + private static List getConfigurables(@Nullable Project project, boolean withIdeSettings, boolean loadComponents) { List list = ContainerUtil.newArrayList(); if (withIdeSettings) { Application application = ApplicationManager.getApplication(); if (application != null) { if (loadComponents) { - ContainerUtil.addAll(list, application.getComponents(Configurable.class)); + addValid(list, application.getComponents(Configurable.class), null); } for (ConfigurableEP extension : application.getExtensions(Configurable.APPLICATION_CONFIGURABLE)) { - ContainerUtil.addIfNotNull(list, ConfigurableWrapper.wrapConfigurable(extension)); + addValid(list, ConfigurableWrapper.wrapConfigurable(extension), null); } } } if (project != null) { if (loadComponents) { - ContainerUtil.addAll(list, project.getComponents(Configurable.class)); + addValid(list, project.getComponents(Configurable.class), project); } for (ConfigurableEP extension : project.getExtensions(Configurable.PROJECT_CONFIGURABLE)) { - ContainerUtil.addIfNotNull(list, ConfigurableWrapper.wrapConfigurable(extension)); + addValid(list, ConfigurableWrapper.wrapConfigurable(extension), project); } } return list; } + private static void addValid(List list, Configurable configurable, Project project) { + if (isValid(configurable, project)) { + list.add(configurable); + } + } + + private static void addValid(List list, Configurable[] configurables, Project project) { + for (Configurable configurable : configurables) { + addValid(list, configurable, project); + } + } + /** * @param configurable settings component to validate * @param project current project, default template project or {@code null} for IDE settings @@ -285,4 +380,37 @@ public class ConfigurableExtensionPointUtil { } return null; } + + /** + * Utility class that helps to build a tree. + */ + private static final class Node { + List myChildren = ContainerUtil.newArrayList(); + Node myParent; + V myValue; + + private static Node get(Map> tree, I id) { + Node node = tree.get(id); + if (node == null) { + node = new Node(); + tree.put(id, node); + } + return node; + } + + private static Node add(Map> tree, I id, Object child) { + Node node = get(tree, id); + node.myChildren.add(child); + return node; + } + + private static boolean cyclic(Map> tree, I id, Node parent) { + for (Node node = tree.get(id); node != null; node = node.myParent) { + if (node == parent) { + return true; + } + } + return false; + } + } }