org.eclipse.jdt.internal.core.dom.rewrite.imports.RemovedImportCommentReassigner Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2015 Google Inc and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* John Glassmyer - import group sorting is broken - https://bugs.eclipse.org/430303
*******************************************************************************/
package org.eclipse.jdt.internal.core.dom.rewrite.imports;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Reassigns comments associated with removed imports (those present before but not present
* after the rewrite) to resultant imports (those present after the rewrite).
*
* Reassigns comments of removed single imports to the first (in iteration order) resultant
* on-demand import having the same container name, if one exists.
*
* Reassigns comments of removed on-demand imports to the first (in iteration order) resultant
* single import having the same container name, if one exists.
*
* Leaves unassigned any removed import comment not matching the above cases.
*/
final class RemovedImportCommentReassigner {
private static Collection retainImportsWithComments(Collection imports) {
Collection importsWithComments = new ArrayList(imports.size());
for (OriginalImportEntry currentImport : imports) {
if (!currentImport.comments.isEmpty()) {
importsWithComments.add(currentImport);
}
}
return importsWithComments;
}
private static boolean hasFloatingComment(OriginalImportEntry nextAssignedImport) {
for (ImportComment importComment : nextAssignedImport.comments) {
if (importComment.succeedingLineDelimiters > 1) {
return true;
}
}
return false;
}
private final Collection originalImportsWithComments;
RemovedImportCommentReassigner(List originalImports) {
this.originalImportsWithComments = retainImportsWithComments(originalImports);
}
/**
* Assigns comments of removed import entries (those in {@code originalImports} but not in
* {@code resultantImports}) to resultant import entries.
*
* Returns a map containing the resulting assignments, where each key is an element of
* {@code resultantImports} and each value is a collection of comments reassigned to that
* resultant import.
*/
Map> reassignComments(Collection resultantImports) {
Map> importAssignments = assignRemovedImports(resultantImports);
Map> commentAssignments =
new HashMap>();
for (Map.Entry> importAssignment : importAssignments.entrySet()) {
ImportEntry targetImport = importAssignment.getKey();
if (targetImport != null) {
Deque assignedComments = new ArrayDeque();
Collection assignedImports = importAssignment.getValue();
Iterator nextAssignedImportIterator = assignedImports.iterator();
if (nextAssignedImportIterator.hasNext()) {
nextAssignedImportIterator.next();
}
Iterator assignedImportIterator = assignedImports.iterator();
while (assignedImportIterator.hasNext()) {
OriginalImportEntry currentAssignedImport = assignedImportIterator.next();
OriginalImportEntry nextAssignedImport =
nextAssignedImportIterator.hasNext() ? nextAssignedImportIterator.next() : null;
assignedComments.addAll(currentAssignedImport.comments);
if (nextAssignedImport != null && hasFloatingComment(nextAssignedImport)) {
// Ensure that a blank line separates this removed import's comments
// from the next removed import's floating comments.
ImportComment lastComment = assignedComments.removeLast();
ImportComment lastCommentWithTrailingBlankLine = new ImportComment(lastComment.region, 2);
assignedComments.add(lastCommentWithTrailingBlankLine);
}
}
commentAssignments.put(targetImport, assignedComments);
}
}
return commentAssignments;
}
private Map> assignRemovedImports(Collection imports) {
Collection removedImportsWithComments = identifyRemovedImportsWithComments(imports);
if (removedImportsWithComments.isEmpty()) {
return Collections.emptyMap();
}
Map firstSingleForOnDemand = identifyFirstSingleForEachOnDemand(imports);
Map firstOccurrences = identifyFirstOccurrenceOfEachImportName(imports);
Map> removedImportsForRetainedImport =
new HashMap>();
for (ImportEntry retainedImport : imports) {
removedImportsForRetainedImport.put(retainedImport, new ArrayList());
}
// The null key will map to the removed imports not assigned to any import.
removedImportsForRetainedImport.put(null, new ArrayList());
for (OriginalImportEntry removedImport : removedImportsWithComments) {
ImportName removedImportName = removedImport.importName;
final ImportEntry retainedImport;
if (removedImportName.isOnDemand()) {
retainedImport = firstSingleForOnDemand.get(removedImportName);
} else {
retainedImport = firstOccurrences.get(removedImportName.getContainerOnDemand());
}
// retainedImport will be null if there's no corresponding import to which to assign the removed import.
removedImportsForRetainedImport.get(retainedImport).add(removedImport);
}
return removedImportsForRetainedImport;
}
private Collection identifyRemovedImportsWithComments(Collection imports) {
Collection removedImports =
new ArrayList(this.originalImportsWithComments);
removedImports.removeAll(imports);
return removedImports;
}
/**
* Assigns each removed on-demand import to the first single import in {@code imports} having
* the same container name.
*
* Returns a map where each key is a single import and each value is the corresponding
* removed on-demand import.
*
* The returned map only contains mappings to removed on-demand imports for which there are
* corresponding single imports in {@code imports}.
*/
private Map identifyFirstSingleForEachOnDemand(Iterable imports) {
Map firstSingleImportForContainer = new HashMap();
for (ImportEntry currentImport : imports) {
if (!currentImport.importName.isOnDemand()) {
ImportName containerOnDemand = currentImport.importName.getContainerOnDemand();
if (!firstSingleImportForContainer.containsKey(containerOnDemand)) {
firstSingleImportForContainer.put(containerOnDemand, currentImport);
}
}
}
return firstSingleImportForContainer;
}
private Map identifyFirstOccurrenceOfEachImportName(Iterable imports) {
Map firstOccurrenceOfImport = new HashMap();
for (ImportEntry resultantImport : imports) {
if (!firstOccurrenceOfImport.containsKey(resultantImport.importName)) {
firstOccurrenceOfImport.put(resultantImport.importName, resultantImport);
}
}
return firstOccurrenceOfImport;
}
}