/*
* Copyright (c) 2013, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.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.eclipse.org/legal/epl-v10.html
*
* 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.dart.engine.services.internal.refactoring;
import com.google.common.base.Objects;
import com.google.dart.engine.element.ClassElement;
import com.google.dart.engine.element.CompilationUnitElement;
import com.google.dart.engine.element.Element;
import com.google.dart.engine.element.ElementKind;
import com.google.dart.engine.element.ImportElement;
import com.google.dart.engine.element.LibraryElement;
import com.google.dart.engine.element.visitor.GeneralizingElementVisitor;
import com.google.dart.engine.search.SearchEngine;
import com.google.dart.engine.search.SearchMatch;
import com.google.dart.engine.services.internal.correction.CorrectionUtils;
import com.google.dart.engine.services.refactoring.ProgressMonitor;
import com.google.dart.engine.services.status.RefactoringStatus;
import com.google.dart.engine.services.status.RefactoringStatusContext;
import static com.google.dart.engine.services.internal.correction.CorrectionUtils.getElementKindName;
import static com.google.dart.engine.services.internal.correction.CorrectionUtils.getElementQualifiedName;
import static com.google.dart.engine.services.internal.correction.CorrectionUtils.hasDisplayName;
import java.text.MessageFormat;
import java.util.List;
/**
* Helper to check if renaming or creating {@link Element} with given name will cause any problems.
*/
class RenameUnitMemberValidator {
private final SearchEngine searchEngine;
private final Element element;
private final ElementKind elementKind;
private final String newName;
public RenameUnitMemberValidator(SearchEngine searchEngine, Element element,
ElementKind elementKind, String newName) {
this.searchEngine = searchEngine;
this.element = element;
this.elementKind = elementKind;
this.newName = newName;
}
public RefactoringStatus validate(ProgressMonitor pm, boolean isRename) {
pm.beginTask("Analyze possible conflicts", 3);
try {
final RefactoringStatus result = new RefactoringStatus();
// check if there are top-level declarations with "newName" in the same LibraryElement
{
LibraryElement library = element.getAncestor(LibraryElement.class);
library.accept(new GeneralizingElementVisitor<Void>() {
@Override
public Void visitElement(Element element) {
// library or unit
if (element instanceof LibraryElement || element instanceof CompilationUnitElement) {
return super.visitElement(element);
}
// top-level
if (hasDisplayName(element, newName)) {
String message = MessageFormat.format(
"Library already declares {0} with name ''{1}''.",
getElementKindName(element),
newName);
result.addError(message, new RefactoringStatusContext(element));
}
return null;
}
});
pm.worked(1);
}
// may be shadowed by class member
if (isRename) {
List<SearchMatch> references = searchEngine.searchReferences(element, null, null);
for (SearchMatch reference : references) {
ClassElement refEnclosingClass = reference.getElement().getAncestor(ClassElement.class);
if (refEnclosingClass != null) {
refEnclosingClass.accept(new GeneralizingElementVisitor<Void>() {
@Override
public Void visitElement(Element maybeShadow) {
// class
if (maybeShadow instanceof ClassElement) {
return super.visitElement(maybeShadow);
}
// class member
if (hasDisplayName(maybeShadow, newName)) {
String message = MessageFormat.format(
"Reference to renamed {0} will shadowed by {1} ''{2}''.",
getElementKindName(elementKind),
getElementKindName(maybeShadow),
getElementQualifiedName(maybeShadow));
result.addError(message, new RefactoringStatusContext(maybeShadow));
}
return null;
}
});
}
}
pm.worked(1);
}
// may be shadows inherited class members
{
List<SearchMatch> nameDeclarations = searchEngine.searchDeclarations(newName, null, null);
for (SearchMatch nameDeclaration : nameDeclarations) {
Element member = nameDeclaration.getElement();
Element declarationClass = member.getEnclosingElement();
if (declarationClass instanceof ClassElement) {
List<SearchMatch> memberReferences = searchEngine.searchReferences(member, null, null);
for (SearchMatch memberReference : memberReferences) {
if (!memberReference.isQualified()) {
Element referenceElement = memberReference.getElement();
ClassElement referenceClass = referenceElement.getAncestor(ClassElement.class);
if (!Objects.equal(referenceClass, declarationClass)) {
if (!isVisibleAt(element, memberReference)) {
continue;
}
String message = MessageFormat.format(
isRename ? "Renamed {0} will shadow {1} ''{2}''."
: "Created {0} will shadow {1} ''{2}''.",
getElementKindName(elementKind),
getElementKindName(member),
getElementQualifiedName(member));
result.addError(message, RefactoringStatusContext.create(memberReference));
}
}
}
}
}
pm.worked(1);
}
// done
return result;
} finally {
pm.done();
}
}
/**
* @return {@code true} if the given {@link Element} is visible at the given {@link SearchMatch}.
*/
private boolean isVisibleAt(Element element, SearchMatch at) {
LibraryElement library = at.getElement().getLibrary();
// may be the same library
if (element.getLibrary().equals(library)) {
return true;
}
// check imports
for (ImportElement importElement : library.getImports()) {
// ignore if imported with prefix
if (importElement.getPrefix() != null) {
continue;
}
// check imported elements
if (CorrectionUtils.getImportNamespace(importElement).containsValue(element)) {
return true;
}
}
// no, it is not visible
return false;
}
}