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

org.eclipse.jdt.internal.core.dom.rewrite.imports.OrderPreservingImportAdder 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.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
import java.util.TreeSet;

/**
 * Keeping existing imports in their existing order, inserts each new import before or after the
 * import to which it would be adjacent if all (existing and new) imports were totally ordered
 * together.
 * 

* A new import that would sort between two existing imports which are not adjacent in the * existing order will be placed adjacent to the existing import with which it shares a longer * prefix of dot-separated name segments. */ final class OrderPreservingImportAdder implements ImportAdder { static class AdjacentImports { final Collection importsBefore = new ArrayList(); final Collection importsAfter = new ArrayList(); @Override public String toString() { return String.format("(%s, %s)", this.importsBefore.toString(), this.importsAfter.toString()); //$NON-NLS-1$ } } /** * Returns the number of prefixing dot-separated segments shared between the two names. *

* For example, {@code countMatchingPrefixSegments("foo.pack1.Class", "foo.pack2.Class")} will * return 1 and {@code countMatchingPrefixSegments("foo.pack1.Class", "com.foo.pack1.Class")} * will return 0. */ private static int countMatchingPrefixSegments(String name1, String name2) { if (name1.isEmpty() || name2.isEmpty()) { return 0; } int matchingSegments = 0; for (int i = 0; i <= name1.length() && i <= name2.length(); i++) { boolean atEndOfName1Segment = i == name1.length() || name1.charAt(i) == '.'; boolean atEndOfName2Segment = i == name2.length() || name2.charAt(i) == '.'; if (atEndOfName1Segment && atEndOfName2Segment) { matchingSegments++; } else if (atEndOfName1Segment || atEndOfName2Segment) { break; } else if (name1.charAt(i) != name2.charAt(i)) { break; } } return matchingSegments; } private final Comparator importComparator; OrderPreservingImportAdder(Comparator importComparator) { this.importComparator = importComparator; } @Override public List addImports(Collection existingImports, Collection importsToAdd) { if (importsToAdd.isEmpty()) { return new ArrayList(existingImports); } List sortedNewImports = new ArrayList(importsToAdd); sortedNewImports.removeAll(new HashSet(existingImports)); Collections.sort(sortedNewImports, this.importComparator); if (existingImports.isEmpty()) { return sortedNewImports; } Map adjacentNewImports = determineAdjacentNewImports(new ArrayList(existingImports), sortedNewImports); List importsWithAdditions = new ArrayList(existingImports.size() + sortedNewImports.size()); for (ImportName existingImport : existingImports) { // Remove the adjacent imports so they don't get inserted multiple times in the case // of duplicate imports. AdjacentImports adjacentImports = adjacentNewImports.remove(existingImport); if (adjacentImports != null) { importsWithAdditions.addAll(adjacentImports.importsBefore); } importsWithAdditions.add(existingImport); if (adjacentImports != null) { importsWithAdditions.addAll(adjacentImports.importsAfter); } } return importsWithAdditions; } /** * Determines which new imports to place before and after each existing import. *

* Returns a Map where each key is an existing import and each corresponding value is an * AdjacentImports containing those new imports which should be placed before and after that * existing import. Each new import will be placed either before or after exactly one existing * import. * * @param existingImports * Existing imports. * @param sortedNewImports * Imports to be added. Must be in order as if sorted by this.importComparator. */ private Map determineAdjacentNewImports( Collection existingImports, Iterable sortedNewImports) { NavigableSet existingImportsTreeSet = new TreeSet(this.importComparator); existingImportsTreeSet.addAll(existingImports); Map adjacentNewImports = new HashMap(); for (ImportName existingImport : existingImports) { adjacentNewImports.put(existingImport, new AdjacentImports()); } for (ImportName newImport : sortedNewImports) { ImportName precedingExistingImport = existingImportsTreeSet.lower(newImport); ImportName succeedingExistingImport = existingImportsTreeSet.higher(newImport); if (shouldGroupWithSucceeding(newImport, precedingExistingImport, succeedingExistingImport)) { adjacentNewImports.get(succeedingExistingImport).importsBefore.add(newImport); } else { adjacentNewImports.get(precedingExistingImport).importsAfter.add(newImport); } } return adjacentNewImports; } /** * Returns true if the new import should be placed before the existing import that would succeed * it in sorted order, or false if the new import should be placed after the existing import * that would precede it in sorted order. */ private boolean shouldGroupWithSucceeding( ImportName newImport, ImportName precedingExistingImport, ImportName succeedingExistingImport) { if (precedingExistingImport == null) { return true; } else if (succeedingExistingImport == null) { return false; } else { String containerName = newImport.containerName; int prefixSharedWithPreceding = countMatchingPrefixSegments(containerName, precedingExistingImport.containerName); int prefixSharedWithSucceeding = countMatchingPrefixSegments(containerName, succeedingExistingImport.containerName); return prefixSharedWithSucceeding > prefixSharedWithPreceding; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy