/* * Copyright 2014 Google Inc. All rights reserved. * * 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 com.google.errorprone.apply; import static com.google.common.base.Preconditions.checkNotNull; import com.google.errorprone.DescriptionListener; import com.google.errorprone.fixes.Fix; import com.google.errorprone.fixes.Replacement; import com.google.errorprone.fixes.Replacements; import com.google.errorprone.matchers.Description; import com.sun.tools.javac.tree.EndPosTable; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import java.util.LinkedHashSet; import java.util.Set; /** * Implementation of a {@link Diff} that performs the modifications that are passed to its * {@link #onDescribed} method, with no formatting. * * <p>If imports are changed, they are resorted as per Google Java style. * * @author lowasser@google.com (Louis Wasserman) */ public final class DescriptionBasedDiff implements DescriptionListener, Diff { private final String sourcePath; private final boolean ignoreOverlappingFixes; private final JCCompilationUnit compilationUnit; private final Set<String> importsToAdd; private final Set<String> importsToRemove; private final EndPosTable endPositions; private final Replacements replacements = new Replacements(); private final ImportOrganizer importOrganizer; public static DescriptionBasedDiff create( JCCompilationUnit compilationUnit, ImportOrganizer importOrganizer) { return new DescriptionBasedDiff(compilationUnit, false, importOrganizer); } public static DescriptionBasedDiff createIgnoringOverlaps( JCCompilationUnit compilationUnit, ImportOrganizer importOrganizer) { return new DescriptionBasedDiff(compilationUnit, true, importOrganizer); } private DescriptionBasedDiff( JCCompilationUnit compilationUnit, boolean ignoreOverlappingFixes, ImportOrganizer importOrganizer) { this.compilationUnit = checkNotNull(compilationUnit); this.sourcePath = compilationUnit.getSourceFile().toUri().getPath(); this.ignoreOverlappingFixes = ignoreOverlappingFixes; this.importsToAdd = new LinkedHashSet<>(); this.importsToRemove = new LinkedHashSet<>(); this.endPositions = compilationUnit.endPositions; this.importOrganizer = importOrganizer; } @Override public String getRelevantFileName() { return sourcePath; } public boolean isEmpty() { return importsToAdd.isEmpty() && importsToRemove.isEmpty() && replacements.isEmpty(); } @Override public void onDescribed(Description description) { // Use only first (most likely) suggested fix if (description.fixes.size() > 0) { handleFix(description.fixes.get(0)); } } public void handleFix(Fix fix) { importsToAdd.addAll(fix.getImportsToAdd()); importsToRemove.addAll(fix.getImportsToRemove()); for (Replacement replacement : fix.getReplacements(endPositions)) { try { replacements.add(replacement); } catch (IllegalArgumentException iae) { if (!ignoreOverlappingFixes) { throw iae; } } } } @Override public void applyDifferences(SourceFile sourceFile) throws DiffNotApplicableException { if (!importsToAdd.isEmpty() || !importsToRemove.isEmpty()) { ImportStatements importStatements = ImportStatements.create(compilationUnit, importOrganizer); importStatements.addAll(importsToAdd); importStatements.removeAll(importsToRemove); replacements.add( Replacement.create( importStatements.getStartPos(), importStatements.getEndPos(), importStatements.toString())); } for (Replacement replacement : replacements.descending()) { sourceFile.replaceChars(replacement.startPosition(), replacement.endPosition(), replacement.replaceWith()); } } }