Copyright (c) 2015 Google Inc and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: John Glassmyer - import group sorting is broken - https://bugs.eclipse.org/430303
/******************************************************************************* * Copyright (c) 2015 Google Inc and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * John Glassmyer <jogl@google.com> - 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.

/** * Reassigns comments associated with removed imports (those present before but not present * after the rewrite) to resultant imports (those present after the rewrite). * <p> * Reassigns comments of removed single imports to the first (in iteration order) resultant * on-demand import having the same container name, if one exists. * <p> * Reassigns comments of removed on-demand imports to the first (in iteration order) resultant * single import having the same container name, if one exists. * <p> * Leaves unassigned any removed import comment not matching the above cases. */
final class RemovedImportCommentReassigner { private static Collection<OriginalImportEntry> retainImportsWithComments(Collection<OriginalImportEntry> imports) { Collection<OriginalImportEntry> importsWithComments = new ArrayList<OriginalImportEntry>(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<OriginalImportEntry> originalImportsWithComments; RemovedImportCommentReassigner(List<OriginalImportEntry> originalImports) { this.originalImportsWithComments = retainImportsWithComments(originalImports); }
Assigns comments of removed import entries (those in originalImports but not in resultantImports) to resultant import entries.

Returns a map containing the resulting assignments, where each key is an element of resultantImports and each value is a collection of comments reassigned to that resultant import.

/** * Assigns comments of removed import entries (those in {@code originalImports} but not in * {@code resultantImports}) to resultant import entries. * <p> * 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<ImportEntry, Collection<ImportComment>> reassignComments(Collection<ImportEntry> resultantImports) { Map<ImportEntry, Collection<OriginalImportEntry>> importAssignments = assignRemovedImports(resultantImports); Map<ImportEntry, Collection<ImportComment>> commentAssignments = new HashMap<ImportEntry, Collection<ImportComment>>(); for (Map.Entry<ImportEntry, Collection<OriginalImportEntry>> importAssignment : importAssignments.entrySet()) { ImportEntry targetImport = importAssignment.getKey(); if (targetImport != null) { Deque<ImportComment> assignedComments = new ArrayDeque<ImportComment>(); Collection<OriginalImportEntry> assignedImports = importAssignment.getValue(); Iterator<OriginalImportEntry> nextAssignedImportIterator = assignedImports.iterator(); if (nextAssignedImportIterator.hasNext()) { nextAssignedImportIterator.next(); } Iterator<OriginalImportEntry> 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<ImportEntry, Collection<OriginalImportEntry>> assignRemovedImports(Collection<ImportEntry> imports) { Collection<OriginalImportEntry> removedImportsWithComments = identifyRemovedImportsWithComments(imports); if (removedImportsWithComments.isEmpty()) { return Collections.emptyMap(); } Map<ImportName, ImportEntry> firstSingleForOnDemand = identifyFirstSingleForEachOnDemand(imports); Map<ImportName, ImportEntry> firstOccurrences = identifyFirstOccurrenceOfEachImportName(imports); Map<ImportEntry, Collection<OriginalImportEntry>> removedImportsForRetainedImport = new HashMap<ImportEntry, Collection<OriginalImportEntry>>(); for (ImportEntry retainedImport : imports) { removedImportsForRetainedImport.put(retainedImport, new ArrayList<OriginalImportEntry>()); } // The null key will map to the removed imports not assigned to any import. removedImportsForRetainedImport.put(null, new ArrayList<OriginalImportEntry>()); 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<OriginalImportEntry> identifyRemovedImportsWithComments(Collection<ImportEntry> imports) { Collection<OriginalImportEntry> removedImports = new ArrayList<OriginalImportEntry>(this.originalImportsWithComments); removedImports.removeAll(imports); return removedImports; }
Assigns each removed on-demand import to the first single import in 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 imports.

/** * Assigns each removed on-demand import to the first single import in {@code imports} having * the same container name. * <p> * Returns a map where each key is a single import and each value is the corresponding * removed on-demand import. * <p> * The returned map only contains mappings to removed on-demand imports for which there are * corresponding single imports in {@code imports}. */
private Map<ImportName, ImportEntry> identifyFirstSingleForEachOnDemand(Iterable<ImportEntry> imports) { Map<ImportName, ImportEntry> firstSingleImportForContainer = new HashMap<ImportName, ImportEntry>(); 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<ImportName, ImportEntry> identifyFirstOccurrenceOfEachImportName(Iterable<ImportEntry> imports) { Map<ImportName, ImportEntry> firstOccurrenceOfImport = new HashMap<ImportName, ImportEntry>(); for (ImportEntry resultantImport : imports) { if (!firstOccurrenceOfImport.containsKey(resultantImport.importName)) { firstOccurrenceOfImport.put(resultantImport.importName, resultantImport); } } return firstOccurrenceOfImport; } }