Configurable refactoring: build tree of configurable (tested)

This commit is contained in:
Sergey Malenkov
2015-04-02 19:18:43 +03:00
parent 2bafe3458e
commit 588427be73
@@ -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<String, List<Configurable>> groupConfigurables(@NotNull List<Configurable> configurables) {
Map<String, Node<ConfigurableWrapper>> tree = ContainerUtil.newHashMap();
for (Configurable configurable : configurables) {
if (configurable instanceof ConfigurableWrapper) {
ConfigurableWrapper wrapper = (ConfigurableWrapper)configurable;
String id = wrapper.getId();
Node<ConfigurableWrapper> 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<String, List<Configurable>> map = ContainerUtil.newHashMap();
for (String id : tree.keySet().toArray(new String[tree.size()])) {
Node<ConfigurableWrapper> node = tree.get(id);
if (node != null) {
List<Configurable> 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<Configurable> getConfigurables(Map<String, Node<ConfigurableWrapper>> tree, Node<ConfigurableWrapper> node) {
List<Configurable> list = ContainerUtil.newArrayListWithCapacity(node.myChildren.size());
for (Iterator<Object> 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<ConfigurableWrapper> value = (Node<ConfigurableWrapper>)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<Configurable> getAllConfigurables(@Nullable Project project, boolean withIdeSettings, boolean loadComponents) {
private static List<Configurable> getConfigurables(@Nullable Project project, boolean withIdeSettings, boolean loadComponents) {
List<Configurable> 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<Configurable> 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<Configurable> 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<Configurable> list, Configurable configurable, Project project) {
if (isValid(configurable, project)) {
list.add(configurable);
}
}
private static void addValid(List<Configurable> 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<V> {
List<Object> myChildren = ContainerUtil.newArrayList();
Node<V> myParent;
V myValue;
private static <I, V> Node<V> get(Map<I, Node<V>> tree, I id) {
Node<V> node = tree.get(id);
if (node == null) {
node = new Node<V>();
tree.put(id, node);
}
return node;
}
private static <I, V> Node<V> add(Map<I, Node<V>> tree, I id, Object child) {
Node<V> node = get(tree, id);
node.myChildren.add(child);
return node;
}
private static <I, V> boolean cyclic(Map<I, Node<V>> tree, I id, Node<V> parent) {
for (Node<V> node = tree.get(id); node != null; node = node.myParent) {
if (node == parent) {
return true;
}
}
return false;
}
}
}