blob: 1155a10839064a5a0ace804822bbce5cc7fa99cf [file] [log] [blame]
/*
* Copyright 2009-2013 Bas Leijdekkers
*
* 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.siyeh.ig.psiutils;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.NotNull;
public class FinalUtils {
private FinalUtils() {}
public static boolean canBeFinal(@NotNull PsiVariable variable) {
final FinalDefiniteAssignment definiteAssignment = new FinalDefiniteAssignment(variable);
DefiniteAssignmentUtil.checkVariable(variable, definiteAssignment);
return definiteAssignment.isDefinitelyAssigned() &&
!definiteAssignment.isDefinitelyUnassigned() && // spec?
definiteAssignment.canBeFinal() &&
!isWrittenToOutsideOfConstruction(variable);
}
private static boolean isWrittenToOutsideOfConstruction(PsiVariable variable) {
if (!(variable instanceof PsiField)) {
return false;
}
final PsiField field = (PsiField)variable;
final PsiClass containingClass = field.getContainingClass();
if (containingClass == null) {
return false;
}
final PsiClass topLevelClass = PsiUtil.getTopLevelClass(variable);
final VariableAssignedVisitor visitor = new VariableAssignedVisitor(field);
if (topLevelClass != null && !containingClass.equals(topLevelClass)) {
visitor.setExcludedElement(containingClass);
topLevelClass.accept(visitor);
if (visitor.isAssigned()) {
return true;
}
}
if (field.hasModifierProperty(PsiModifier.STATIC)) {
for (PsiElement child : containingClass.getChildren()) {
if (child instanceof PsiClassInitializer) {
final PsiClassInitializer classInitializer = (PsiClassInitializer)child;
if (classInitializer.hasModifierProperty(PsiModifier.STATIC)) {
continue;
}
classInitializer.accept(visitor);
}
else if (child instanceof PsiField) {
final PsiField otherField = (PsiField)child;
if (otherField.hasModifierProperty(PsiModifier.STATIC)) {
continue;
}
otherField.accept(visitor);
}
else if (child instanceof PsiMethod || child instanceof PsiClass) {
child.accept(visitor);
}
if (visitor.isAssigned()) {
return true;
}
}
}
else {
for (PsiElement child : containingClass.getChildren()) {
if (child instanceof PsiField) {
final PsiField otherField = (PsiField)child;
if (!otherField.hasModifierProperty(PsiModifier.STATIC)) {
continue;
}
otherField.accept(visitor);
}
else if (child instanceof PsiClassInitializer) {
final PsiClassInitializer classInitializer = (PsiClassInitializer)child;
if (!classInitializer.hasModifierProperty(PsiModifier.STATIC)) {
continue;
}
classInitializer.accept(visitor);
}
else if (child instanceof PsiMethod) {
final PsiMethod method = (PsiMethod)child;
if (method.isConstructor()) {
continue;
}
method.accept(visitor);
}
else if (child instanceof PsiClass) {
child.accept(visitor);
}
if (visitor.isAssigned()) {
return true;
}
}
}
return false;
}
private static class FinalDefiniteAssignment extends DefiniteAssignment {
private boolean canBeFinal = true;
public FinalDefiniteAssignment(PsiVariable variable) {
super(variable);
}
@Override
public void assign(@NotNull PsiReferenceExpression expression, boolean definiteAssignment) {
if (!isDefinitelyUnassigned()) {
canBeFinal = false;
}
super.assign(expression, definiteAssignment);
}
@Override
public void valueAccess(PsiReferenceExpression expression) {
if (!isDefinitelyAssigned()) {
canBeFinal = false;
}
super.valueAccess(expression);
}
@Override
public boolean stop() {
return !canBeFinal;
}
public boolean canBeFinal() {
return canBeFinal;
}
}
}