All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.gradle.plugins.ide.internal.configurer.HierarchicalElementDeduplicator Maven / Gradle / Ivy

There is a newer version: 8.11.1
Show newest version
/*
 * Copyright 2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.gradle.plugins.ide.internal.configurer;

import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.common.primitives.Ints;

import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * A generic name de-duplicator for hierarchical elements.
 * 

* Conflicting sub-elements are de-duplicated by prepending their parent element names, separated by a dash. * Conflicting root elements are rejected with an {@link IllegalArgumentException} *

* If a child's simple name already contains the name of its parent, the two prefixes are collapsed to keep names short. * For example, an elements with the name segments root:impl:impl-simple would initially get the name * root-impl-impl-simple and would then be shortened to root-impl-simple * This shortening is of course only applied if it does not introduce a new name conflict. * * @param the type of element to de-duplicate */ public class HierarchicalElementDeduplicator { private final HierarchicalElementAdapter adapter; public HierarchicalElementDeduplicator(HierarchicalElementAdapter adapter) { this.adapter = adapter; } /** * Calculates a set of renamings for each duplicate name in the given set of elements. * * @param elements the elements with possibly duplicated names * @return a Map containing the new name for each element that has to be renamed */ public Map deduplicate(Iterable elements) { return new StatefulDeduplicator(elements).getNewNames(); } /* * This inner class hides the fact that the actual de-duplication algorithm is stateful. */ private class StatefulDeduplicator { private final List elements; private final Multimap elementsByName; private final Map newNames; private final Map prefixes; private StatefulDeduplicator(Iterable elements) { this.elements = Lists.newArrayList(elements); this.elementsByName = LinkedHashMultimap.create(); this.newNames = Maps.newHashMap(); this.prefixes = Maps.newHashMap(); } private Map getNewNames() { if (!elements.isEmpty() && newNames.isEmpty()) { calculateNewNames(); } return ImmutableMap.copyOf(newNames); } private void calculateNewNames() { sortElementsByDepth(); for (T element : elements) { elementsByName.put(getOriginalName(element), element); prefixes.put(element, getParent(element)); } while (!getDuplicateNames().isEmpty()) { deduplicate(); } simplifyNames(); } private void deduplicate() { for (String duplicateName : getDuplicateNames()) { Collection duplicatedElements = elementsByName.get(duplicateName); Set reservedNames = ImmutableSet.copyOf(elementsByName.keySet()); Set notYetRenamed = getNotYetRenamedElements(duplicatedElements); boolean deduplicationSuccessful = false; Set elementsToRename = notYetRenamed.isEmpty() ? ImmutableSet.copyOf(duplicatedElements) : notYetRenamed; for (T element : elementsToRename) { boolean elementRenamed = true; while (elementRenamed && reservedNames.contains(getCurrentlyAssignedName(element))) { elementRenamed = renameUsingParentPrefix(element); deduplicationSuccessful |= elementRenamed; } } if (!deduplicationSuccessful) { throw new IllegalArgumentException("Duplicate root element " + duplicateName); } } } private boolean renameUsingParentPrefix(T element) { T prefixElement = prefixes.get(element); if (prefixElement != null) { renameTo(element, getOriginalName(prefixElement) + "-" + getCurrentlyAssignedName(element)); prefixes.put(element, getParent(prefixElement)); return true; } return false; } private void renameTo(T element, String newName) { elementsByName.remove(getCurrentlyAssignedName(element), element); elementsByName.put(newName, element); newNames.put(element, newName); } private void simplifyNames() { Set deduplicatedNames = elementsByName.keySet(); for (T element : elements) { String simplifiedName = removeDuplicateWordsFromPrefix(getCurrentlyAssignedName(element), getOriginalName(element)); if (!deduplicatedNames.contains(simplifiedName)) { renameTo(element, simplifiedName); } } } private String removeDuplicateWordsFromPrefix(String deduplicatedName, String originalName) { String prefix = deduplicatedName.substring(0, deduplicatedName.lastIndexOf(originalName)); if (prefix.isEmpty()) { return deduplicatedName; } Splitter splitter = Splitter.on('-').omitEmptyStrings(); List prefixParts = Lists.newArrayList(splitter.split(prefix)); List postfixParts = Lists.newArrayList(splitter.split(originalName)); List words = Lists.newArrayList(); if (postfixParts.size() > 1) { String postfixHead = postfixParts.get(0); prefixParts.add(postfixHead); postfixParts.remove(postfixHead); } for (String prefixPart : prefixParts) { if (!prefixPart.equals(Iterables.getLast(words, null))) { words.add(prefixPart); } } words.addAll(postfixParts); return Joiner.on('-').join(words); } private Set getDuplicateNames() { Set duplicates = Sets.newLinkedHashSet(); for (Entry> entry : elementsByName.asMap().entrySet()) { if (entry.getValue().size() > 1) { duplicates.add(entry.getKey()); } } return duplicates; } private Set getNotYetRenamedElements(Collection elementsToRename) { Set notYetRenamed = Sets.newLinkedHashSet(); for (T element : elementsToRename) { if (!hasBeenRenamed(element)) { notYetRenamed.add(element); } } return notYetRenamed; } private String getOriginalName(T element) { return adapter.getName(element); } private String getCurrentlyAssignedName(T element) { if (hasBeenRenamed(element)) { return newNames.get(element); } else { return getOriginalName(element); } } private T getParent(T parent) { return adapter.getParent(parent); } private boolean hasBeenRenamed(T element) { return newNames.containsKey(element); } private void sortElementsByDepth() { Collections.sort(elements, new Comparator() { @Override public int compare(T left, T right) { return Ints.compare(getDepth(left), getDepth(right)); } private int getDepth(T element) { int depth = 0; T parent = element; while (parent != null) { depth++; parent = getParent(parent); } return depth; } }); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy