mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
+1
-1
@@ -43,7 +43,7 @@ public class AnnotationsAwareDataFlowRunner extends DataFlowRunner {
|
||||
//todo move out from generic runner
|
||||
for (PsiParameter parameter : method.getParameterList().getParameters()) {
|
||||
if (NullableNotNullManager.isNotNull(parameter)) {
|
||||
final DfaVariableValue value = getFactory().getVarFactory().create(parameter, false);
|
||||
final DfaVariableValue value = getFactory().getVarFactory().createVariableValue(parameter, false);
|
||||
for (final DfaMemoryState initialState : initialStates) {
|
||||
initialState.applyNotNull(value);
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ public class ControlFlow {
|
||||
}
|
||||
|
||||
public void removeVariable(PsiVariable variable) {
|
||||
DfaVariableValue var = myFactory.getVarFactory().create(variable, false);
|
||||
DfaVariableValue var = myFactory.getVarFactory().createVariableValue(variable, false);
|
||||
addInstruction(new FlushVariableInstruction(var));
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,10 @@ package com.intellij.codeInspection.dataFlow;
|
||||
import com.intellij.codeInsight.ExceptionUtil;
|
||||
import com.intellij.codeInsight.NullableNotNullManager;
|
||||
import com.intellij.codeInspection.dataFlow.instructions.*;
|
||||
import com.intellij.codeInspection.dataFlow.value.*;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaUnknownValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.psi.*;
|
||||
@@ -27,6 +30,7 @@ import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.RedundantCastUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.refactoring.psi.PropertyUtils;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.containers.Stack;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
@@ -232,7 +236,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
}
|
||||
|
||||
private void initializeVariable(PsiVariable variable, PsiExpression initializer) {
|
||||
DfaVariableValue dfaVariable = myFactory.getVarFactory().create(variable, false);
|
||||
DfaVariableValue dfaVariable = myFactory.getVarFactory().createVariableValue(variable, false);
|
||||
addInstruction(new PushInstruction(dfaVariable, initializer));
|
||||
initializer.accept(this);
|
||||
generateBoxingUnboxingInstructionFor(initializer, variable.getType());
|
||||
@@ -367,7 +371,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
}
|
||||
|
||||
int offset = myCurrentFlow.getInstructionCount();
|
||||
DfaVariableValue dfaVariable = myFactory.getVarFactory().create(parameter, false);
|
||||
DfaVariableValue dfaVariable = myFactory.getVarFactory().createVariableValue(parameter, false);
|
||||
addInstruction(new PushInstruction(dfaVariable, null));
|
||||
pushUnknown();
|
||||
addInstruction(new AssignInstruction(null));
|
||||
@@ -684,7 +688,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
* @param cd
|
||||
*/
|
||||
private void addGotoCatch(CatchDescriptor cd) {
|
||||
addInstruction(new PushInstruction(myFactory.getVarFactory().create(cd.getParameter(), false), null));
|
||||
addInstruction(new PushInstruction(myFactory.getVarFactory().createVariableValue(cd.getParameter(), false), null));
|
||||
addInstruction(new SwapInstruction());
|
||||
myCurrentFlow.addInstruction(new AssignInstruction(null));
|
||||
addInstruction(new PopInstruction());
|
||||
@@ -1191,7 +1195,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
addInstruction(new MethodCallInstruction(expression));
|
||||
addInstruction(new MethodCallInstruction(expression, createChainedVariableValue(expression)));
|
||||
|
||||
if (!myCatchStack.isEmpty()) {
|
||||
addMethodThrows(expression.resolveMethod());
|
||||
@@ -1357,7 +1361,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
addInstruction(new PopInstruction());
|
||||
}
|
||||
}
|
||||
addInstruction(new MethodCallInstruction(expression));
|
||||
addInstruction(new MethodCallInstruction(expression, (DfaValue)null));
|
||||
}
|
||||
else {
|
||||
final PsiExpressionList args = expression.getArgumentList();
|
||||
@@ -1374,7 +1378,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
addInstruction(new MethodCallInstruction(expression));
|
||||
addInstruction(new MethodCallInstruction(expression, (DfaValue)null));
|
||||
|
||||
if (!myCatchStack.isEmpty()) {
|
||||
addMethodThrows(ctr);
|
||||
@@ -1410,7 +1414,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
if (operand instanceof PsiReferenceExpression) {
|
||||
PsiVariable psiVariable = DfaValueFactory.resolveVariable((PsiReferenceExpression)expression.getOperand());
|
||||
if (psiVariable != null) {
|
||||
DfaVariableValue dfaVariable = myFactory.getVarFactory().create(psiVariable, false);
|
||||
DfaVariableValue dfaVariable = myFactory.getVarFactory().createVariableValue(psiVariable, false);
|
||||
addInstruction(new FlushVariableInstruction(dfaVariable));
|
||||
}
|
||||
}
|
||||
@@ -1443,7 +1447,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
if (operand instanceof PsiReferenceExpression) {
|
||||
PsiVariable psiVariable = DfaValueFactory.resolveVariable((PsiReferenceExpression)operand);
|
||||
if (psiVariable != null) {
|
||||
DfaVariableValue dfaVariable = myFactory.getVarFactory().create(psiVariable, false);
|
||||
DfaVariableValue dfaVariable = myFactory.getVarFactory().createVariableValue(psiVariable, false);
|
||||
addInstruction(new FlushVariableInstruction(dfaVariable));
|
||||
}
|
||||
}
|
||||
@@ -1460,40 +1464,92 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
@Override public void visitReferenceExpression(PsiReferenceExpression expression) {
|
||||
startElement(expression);
|
||||
|
||||
DfaValue dfaValue = myFactory.create(expression);
|
||||
PsiElement resolved = expression.resolve();
|
||||
if (dfaValue instanceof DfaVariableValue) {
|
||||
DfaVariableValue dfaVariable = (DfaVariableValue)dfaValue;
|
||||
PsiVariable psiVariable = dfaVariable.getPsiVariable();
|
||||
if (psiVariable instanceof PsiField) {
|
||||
addField(dfaVariable);
|
||||
}
|
||||
}
|
||||
|
||||
final PsiExpression qualifierExpression = expression.getQualifierExpression();
|
||||
if (qualifierExpression != null) {
|
||||
qualifierExpression.accept(this);
|
||||
if (resolved instanceof PsiField) {
|
||||
addInstruction(new FieldReferenceInstruction(expression, null));
|
||||
}
|
||||
else {
|
||||
addInstruction(new PopInstruction());
|
||||
}
|
||||
addInstruction(expression.resolve() instanceof PsiField ? new FieldReferenceInstruction(expression, null) : new PopInstruction());
|
||||
}
|
||||
|
||||
if (dfaValue == null && resolved instanceof PsiField) {
|
||||
// Accessing a field from another instance
|
||||
dfaValue = myFactory.getTypeFactory().create(((PsiField)resolved).getType(),
|
||||
NullableNotNullManager.isNullable((PsiModifierListOwner)resolved));
|
||||
}
|
||||
|
||||
addInstruction(new PushInstruction(dfaValue, expression));
|
||||
addInstruction(new PushInstruction(getExpressionDfaValue(expression), expression));
|
||||
|
||||
finishElement(expression);
|
||||
}
|
||||
|
||||
private void addField(DfaVariableValue field) {
|
||||
myFields.add(field);
|
||||
@Nullable
|
||||
private DfaValue getExpressionDfaValue(PsiReferenceExpression expression) {
|
||||
DfaValue dfaValue = myFactory.create(expression);
|
||||
if (dfaValue instanceof DfaVariableValue) {
|
||||
DfaVariableValue dfaVariable = (DfaVariableValue)dfaValue;
|
||||
if (dfaVariable.getPsiVariable() instanceof PsiField) {
|
||||
myFields.add(dfaVariable);
|
||||
}
|
||||
}
|
||||
if (dfaValue == null) {
|
||||
PsiElement resolved = expression.resolve();
|
||||
if (resolved instanceof PsiField) {
|
||||
dfaValue = createDfaValueForAnotherInstanceMemberAccess(expression, (PsiField)resolved);
|
||||
}
|
||||
}
|
||||
return dfaValue;
|
||||
}
|
||||
|
||||
private DfaValue createDfaValueForAnotherInstanceMemberAccess(PsiReferenceExpression expression, PsiField field) {
|
||||
DfaValue dfaValue = null;
|
||||
if (expression.getQualifierExpression() != null) {
|
||||
dfaValue = createChainedVariableValue(expression);
|
||||
}
|
||||
if (dfaValue == null) {
|
||||
return myFactory.getTypeFactory().create(field.getType(), NullableNotNullManager.isNullable(field));
|
||||
}
|
||||
return dfaValue;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private DfaVariableValue createChainedVariableValue(@Nullable PsiExpression expression) {
|
||||
if (expression instanceof PsiParenthesizedExpression) {
|
||||
return createChainedVariableValue(((PsiParenthesizedExpression)expression).getExpression());
|
||||
}
|
||||
|
||||
PsiReferenceExpression refExpr;
|
||||
if (expression instanceof PsiMethodCallExpression) {
|
||||
refExpr = ((PsiMethodCallExpression)expression).getMethodExpression();
|
||||
}
|
||||
else if (expression instanceof PsiReferenceExpression) {
|
||||
refExpr = (PsiReferenceExpression)expression;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiVariable var = resolveToVariable(refExpr);
|
||||
if (var == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiExpression qualifier = refExpr.getQualifierExpression();
|
||||
if (qualifier == null) {
|
||||
return myFactory.getVarFactory().createVariableValue(var, false, null);
|
||||
}
|
||||
|
||||
if (var instanceof PsiField && var.hasModifierProperty(PsiModifier.FINAL)) {
|
||||
DfaVariableValue qualifierValue = createChainedVariableValue(qualifier);
|
||||
if (qualifierValue != null) {
|
||||
return myFactory.getVarFactory().createVariableValue(var, false, qualifierValue);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiVariable resolveToVariable(PsiReferenceExpression refExpr) {
|
||||
PsiElement target = refExpr.resolve();
|
||||
if (target instanceof PsiVariable) {
|
||||
return (PsiVariable)target;
|
||||
}
|
||||
if (target instanceof PsiMethod) {
|
||||
return PropertyUtils.getSimplyReturnedField((PsiMethod)target, PropertyUtils.getSingleReturnValue((PsiMethod)target));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public void visitSuperExpression(PsiSuperExpression expression) {
|
||||
|
||||
@@ -232,14 +232,12 @@ public class DataFlowInspection extends BaseLocalInspectionTool {
|
||||
createSimplifyToAssignmentFix()
|
||||
);
|
||||
}
|
||||
else {
|
||||
boolean report = !(psiAnchor.getParent() instanceof PsiAssertStatement) || !DONT_REPORT_TRUE_ASSERT_STATEMENTS || !evaluatesToTrue;
|
||||
if (report) {
|
||||
final LocalQuickFix localQuickFix = createSimplifyBooleanExpressionFix(psiAnchor, evaluatesToTrue);
|
||||
holder.registerProblem(psiAnchor, InspectionsBundle.message(underBinary ? "dataflow.message.constant.condition.whenriched" : "dataflow.message.constant.condition",
|
||||
Boolean.toString(evaluatesToTrue)),
|
||||
localQuickFix == null ? null : new LocalQuickFix[]{localQuickFix});
|
||||
}
|
||||
else if (shouldReportConditionAlwaysTrueOrFalse(psiAnchor, evaluatesToTrue)) {
|
||||
final LocalQuickFix fix = createSimplifyBooleanExpressionFix(psiAnchor, evaluatesToTrue);
|
||||
String message = InspectionsBundle.message(underBinary ?
|
||||
"dataflow.message.constant.condition.whenriched" :
|
||||
"dataflow.message.constant.condition", Boolean.toString(evaluatesToTrue));
|
||||
holder.registerProblem(psiAnchor, message, fix == null ? null : new LocalQuickFix[]{fix});
|
||||
}
|
||||
reportedAnchors.add(psiAnchor);
|
||||
}
|
||||
@@ -287,6 +285,13 @@ public class DataFlowInspection extends BaseLocalInspectionTool {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldReportConditionAlwaysTrueOrFalse(PsiElement psiAnchor, boolean evaluatesToTrue) {
|
||||
if (psiAnchor.getParent() instanceof PsiAssertStatement && DONT_REPORT_TRUE_ASSERT_STATEMENTS && evaluatesToTrue) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isAtRHSOfBooleanAnd(PsiElement expr) {
|
||||
PsiElement cur = expr;
|
||||
|
||||
|
||||
@@ -741,16 +741,23 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
return;
|
||||
}
|
||||
|
||||
doFlush(variable);
|
||||
flushWithDependencies(variable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushVariableOutOfScope(DfaVariableValue variable) {
|
||||
flushWithDependencies(variable);
|
||||
}
|
||||
|
||||
private void flushWithDependencies(DfaVariableValue variable) {
|
||||
doFlush(variable);
|
||||
for (DfaVariableValue dependent : myFactory.getVarFactory().getAllQualifiedBy(variable)) {
|
||||
doFlush(dependent);
|
||||
}
|
||||
}
|
||||
|
||||
private void doFlush(DfaVariableValue varPlain) {
|
||||
DfaVariableValue varNegated = (DfaVariableValue)varPlain.createNegated();
|
||||
DfaVariableValue varNegated = varPlain.createNegated();
|
||||
|
||||
final int idPlain = varPlain.getID();
|
||||
final int idNegated = varNegated.getID();
|
||||
|
||||
+34
-26
@@ -189,48 +189,56 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
return nextInstruction(instruction, runner, memState);
|
||||
}
|
||||
finally {
|
||||
pushResult(instruction, memState, qualifier, runner.getFactory());
|
||||
memState.push(getMethodResultValue(instruction, qualifier, runner.getFactory()));
|
||||
if (instruction.shouldFlushFields()) {
|
||||
memState.flushFields(runner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void pushResult(MethodCallInstruction instruction, DfaMemoryState state, final DfaValue oldValue, DfaValueFactory factory) {
|
||||
@NotNull
|
||||
private DfaValue getMethodResultValue(MethodCallInstruction instruction, @NotNull DfaValue qualifierValue, DfaValueFactory factory) {
|
||||
DfaValue precalculated = instruction.getPrecalculatedReturnValue();
|
||||
if (precalculated != null) {
|
||||
return precalculated;
|
||||
}
|
||||
|
||||
final PsiType type = instruction.getResultType();
|
||||
final MethodCallInstruction.MethodType methodType = instruction.getMethodType();
|
||||
DfaValue dfaValue = null;
|
||||
if (type != null && (type instanceof PsiClassType || type.getArrayDimensions() > 0)) {
|
||||
@Nullable final Boolean nullability = myCalleeNullability.get(instruction);
|
||||
dfaValue = nullability == Boolean.FALSE ? factory.getNotNullFactory().create(type) : factory.getTypeFactory().create(type, nullability == Boolean.TRUE);
|
||||
return nullability == Boolean.FALSE ? factory.getNotNullFactory().create(type) : factory.getTypeFactory().create(type, nullability == Boolean.TRUE);
|
||||
}
|
||||
else if (methodType == MethodCallInstruction.MethodType.UNBOXING) {
|
||||
dfaValue = factory.getBoxedFactory().createUnboxed(oldValue);
|
||||
}
|
||||
else if (methodType == MethodCallInstruction.MethodType.BOXING) {
|
||||
dfaValue = factory.getBoxedFactory().createBoxed(oldValue);
|
||||
}
|
||||
else if (methodType == MethodCallInstruction.MethodType.CAST) {
|
||||
if (oldValue instanceof DfaConstValue) {
|
||||
final DfaConstValue constValue = (DfaConstValue)oldValue;
|
||||
Object o = constValue.getValue();
|
||||
if (o instanceof Double || o instanceof Float) {
|
||||
double dbVal = o instanceof Double ? ((Double)o).doubleValue() : ((Float)o).doubleValue();
|
||||
// 5.0f == 5
|
||||
if (Math.floor(dbVal) == dbVal) o = TypeConversionUtil.computeCastTo(o, PsiType.LONG);
|
||||
}
|
||||
else {
|
||||
o = TypeConversionUtil.computeCastTo(o, PsiType.LONG);
|
||||
}
|
||||
|
||||
dfaValue = factory.getConstFactory().createFromValue(o, type);
|
||||
if (methodType == MethodCallInstruction.MethodType.UNBOXING) {
|
||||
return factory.getBoxedFactory().createUnboxed(qualifierValue);
|
||||
}
|
||||
|
||||
if (methodType == MethodCallInstruction.MethodType.BOXING) {
|
||||
DfaValue boxed = factory.getBoxedFactory().createBoxed(qualifierValue);
|
||||
return boxed == null ? DfaUnknownValue.getInstance() : boxed;
|
||||
}
|
||||
|
||||
if (methodType == MethodCallInstruction.MethodType.CAST) {
|
||||
if (qualifierValue instanceof DfaConstValue) {
|
||||
return factory.getConstFactory().createFromValue(castConstValue((DfaConstValue)qualifierValue), type);
|
||||
}
|
||||
else {
|
||||
dfaValue = oldValue;
|
||||
return qualifierValue;
|
||||
}
|
||||
return DfaUnknownValue.getInstance();
|
||||
}
|
||||
|
||||
private static Object castConstValue(DfaConstValue constValue) {
|
||||
Object o = constValue.getValue();
|
||||
if (o instanceof Double || o instanceof Float) {
|
||||
double dbVal = o instanceof Double ? ((Double)o).doubleValue() : ((Float)o).doubleValue();
|
||||
// 5.0f == 5
|
||||
if (Math.floor(dbVal) != dbVal) {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
state.push(dfaValue == null ? DfaUnknownValue.getInstance() : dfaValue);
|
||||
return TypeConversionUtil.computeCastTo(o, PsiType.LONG);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+10
-2
@@ -28,6 +28,7 @@ import com.intellij.codeInspection.dataFlow.DataFlowRunner;
|
||||
import com.intellij.codeInspection.dataFlow.DfaInstructionState;
|
||||
import com.intellij.codeInspection.dataFlow.DfaMemoryState;
|
||||
import com.intellij.codeInspection.dataFlow.InstructionVisitor;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.psi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -40,15 +41,17 @@ public class MethodCallInstruction extends Instruction {
|
||||
private boolean myShouldFlushFields;
|
||||
@NotNull private final PsiExpression myContext;
|
||||
private final MethodType myMethodType;
|
||||
@Nullable private DfaValue myPrecalculatedReturnValue;
|
||||
public static enum MethodType {
|
||||
BOXING, UNBOXING, REGULAR_METHOD_CALL, CAST
|
||||
}
|
||||
|
||||
public MethodCallInstruction(@NotNull PsiCallExpression callExpression) {
|
||||
public MethodCallInstruction(@NotNull PsiCallExpression callExpression, @Nullable DfaValue precalculatedReturnValue) {
|
||||
this(callExpression, MethodType.REGULAR_METHOD_CALL);
|
||||
myPrecalculatedReturnValue = precalculatedReturnValue;
|
||||
}
|
||||
|
||||
public MethodCallInstruction(@NotNull PsiExpression context, MethodType methodType, PsiType resultType) {
|
||||
public MethodCallInstruction(@NotNull PsiExpression context, MethodType methodType, @Nullable PsiType resultType) {
|
||||
this(context, methodType);
|
||||
myType = resultType;
|
||||
myShouldFlushFields = false;
|
||||
@@ -102,6 +105,11 @@ public class MethodCallInstruction extends Instruction {
|
||||
return myContext;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DfaValue getPrecalculatedReturnValue() {
|
||||
return myPrecalculatedReturnValue;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return myMethodType == MethodType.UNBOXING
|
||||
? "UNBOX"
|
||||
|
||||
@@ -40,7 +40,6 @@ public class DfaUnboxedValue extends DfaValue {
|
||||
}
|
||||
|
||||
public DfaValue createNegated() {
|
||||
DfaVariableValue negVar = myFactory.getVarFactory().create(myVariable.getPsiVariable(), !myVariable.isNegated());
|
||||
return myFactory.getBoxedFactory().createUnboxed(negVar);
|
||||
return myFactory.getBoxedFactory().createUnboxed(myVariable.createNegated());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ public class DfaValueFactory {
|
||||
|
||||
PsiVariable psiVariable = resolveVariable((PsiReferenceExpression)psiExpression);
|
||||
if (psiVariable != null) {
|
||||
result = getVarFactory().create(psiVariable, false);
|
||||
result = getVarFactory().createVariableValue(psiVariable, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+36
-7
@@ -26,15 +26,18 @@ package com.intellij.codeInspection.dataFlow.value;
|
||||
|
||||
import com.intellij.psi.PsiVariable;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DfaVariableValue extends DfaValue {
|
||||
public static class Factory {
|
||||
private final DfaVariableValue mySharedInstance;
|
||||
private final HashMap<String,ArrayList<DfaVariableValue>> myStringToObject;
|
||||
private final DfaValueFactory myFactory;
|
||||
private final MultiMap<DfaVariableValue, DfaVariableValue> myQualifiersToChainedVariables = new MultiMap<DfaVariableValue, DfaVariableValue>();
|
||||
|
||||
Factory(DfaValueFactory factory) {
|
||||
myFactory = factory;
|
||||
@@ -42,9 +45,13 @@ public class DfaVariableValue extends DfaValue {
|
||||
myStringToObject = new HashMap<String, ArrayList<DfaVariableValue>>();
|
||||
}
|
||||
|
||||
public DfaVariableValue create(PsiVariable myVariable, boolean isNegated) {
|
||||
public DfaVariableValue createVariableValue(PsiVariable myVariable, boolean isNegated) {
|
||||
return createVariableValue(myVariable, isNegated, null);
|
||||
}
|
||||
public DfaVariableValue createVariableValue(PsiVariable myVariable, boolean isNegated, @Nullable DfaVariableValue qualifier) {
|
||||
mySharedInstance.myVariable = myVariable;
|
||||
mySharedInstance.myIsNegated = isNegated;
|
||||
mySharedInstance.myQualifier = qualifier;
|
||||
|
||||
String id = mySharedInstance.toString();
|
||||
ArrayList<DfaVariableValue> conditions = myStringToObject.get(id);
|
||||
@@ -58,19 +65,34 @@ public class DfaVariableValue extends DfaValue {
|
||||
}
|
||||
}
|
||||
|
||||
DfaVariableValue result = new DfaVariableValue(myVariable, isNegated, myFactory);
|
||||
DfaVariableValue result = new DfaVariableValue(myVariable, isNegated, myFactory, qualifier);
|
||||
if (qualifier != null) {
|
||||
myQualifiersToChainedVariables.putValue(qualifier, result);
|
||||
}
|
||||
conditions.add(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<DfaVariableValue> getAllQualifiedBy(DfaVariableValue value) {
|
||||
ArrayList<DfaVariableValue> result = new ArrayList<DfaVariableValue>();
|
||||
for (DfaVariableValue directQualified : myQualifiersToChainedVariables.get(value)) {
|
||||
result.add(directQualified);
|
||||
result.addAll(getAllQualifiedBy(directQualified));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private PsiVariable myVariable;
|
||||
@Nullable private DfaVariableValue myQualifier;
|
||||
private boolean myIsNegated;
|
||||
|
||||
private DfaVariableValue(PsiVariable variable, boolean isNegated, DfaValueFactory factory) {
|
||||
private DfaVariableValue(PsiVariable variable, boolean isNegated, DfaValueFactory factory, @Nullable DfaVariableValue qualifier) {
|
||||
super(factory);
|
||||
myVariable = variable;
|
||||
myIsNegated = isNegated;
|
||||
myQualifier = qualifier;
|
||||
}
|
||||
|
||||
private DfaVariableValue(DfaValueFactory factory) {
|
||||
@@ -88,17 +110,24 @@ public class DfaVariableValue extends DfaValue {
|
||||
return myIsNegated;
|
||||
}
|
||||
|
||||
public DfaValue createNegated() {
|
||||
return myFactory.getVarFactory().create(getPsiVariable(), !myIsNegated);
|
||||
public DfaVariableValue createNegated() {
|
||||
return myFactory.getVarFactory().createVariableValue(myVariable, !myIsNegated, myQualifier);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"HardCodedStringLiteral"})
|
||||
public String toString() {
|
||||
if (myVariable == null) return "$currentException";
|
||||
return (myIsNegated ? "!" : "") + myVariable.getName();
|
||||
return (myIsNegated ? "!" : "") + myVariable.getName() + (myQualifier == null ? "" : "|" + myQualifier.toString());
|
||||
}
|
||||
|
||||
private boolean hardEquals(DfaVariableValue aVar) {
|
||||
return aVar.myVariable == myVariable && aVar.myIsNegated == myIsNegated;
|
||||
return aVar.myVariable == myVariable &&
|
||||
aVar.myIsNegated == myIsNegated &&
|
||||
(myQualifier == null ? aVar.myQualifier == null : myQualifier.hardEquals(aVar.myQualifier));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DfaVariableValue getQualifier() {
|
||||
return myQualifier;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,84 +52,68 @@ public class PropertyUtils {
|
||||
*/
|
||||
@Nullable
|
||||
public static PsiExpression getGetterReturnExpression(PsiMethod method) {
|
||||
if (method == null) {
|
||||
return null;
|
||||
}
|
||||
final PsiParameterList parameterList = method.getParameterList();
|
||||
if (parameterList.getParametersCount() != 0) {
|
||||
return null;
|
||||
}
|
||||
@NonNls final String name = method.getName();
|
||||
if (!name.startsWith("get") && !name.startsWith("is")) {
|
||||
return null;
|
||||
}
|
||||
if (method.hasModifierProperty(PsiModifier.SYNCHRONIZED)) {
|
||||
return null;
|
||||
}
|
||||
return method != null && hasGetterSignature(method) ? getSingleReturnValue(method) : null;
|
||||
}
|
||||
|
||||
private static boolean hasGetterSignature(@NotNull PsiMethod method) {
|
||||
return PropertyUtil.isSimplePropertyGetter(method) && !method.hasModifierProperty(PsiModifier.SYNCHRONIZED);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiExpression getSingleReturnValue(@NotNull PsiMethod method) {
|
||||
final PsiCodeBlock body = method.getBody();
|
||||
if (body == null) {
|
||||
return null;
|
||||
}
|
||||
final PsiStatement[] statements = body.getStatements();
|
||||
if (statements.length != 1) {
|
||||
return null;
|
||||
}
|
||||
final PsiStatement statement = statements[0];
|
||||
if (!(statement instanceof PsiReturnStatement)) {
|
||||
return null;
|
||||
}
|
||||
final PsiReturnStatement returnStatement =
|
||||
(PsiReturnStatement)statement;
|
||||
final PsiExpression value = returnStatement.getReturnValue();
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
final PsiStatement statement = statements.length != 1 ? null : statements[0];
|
||||
return statement instanceof PsiReturnStatement ? ((PsiReturnStatement)statement).getReturnValue() : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiField getFieldOfGetter(PsiMethod method) {
|
||||
final PsiExpression value = getGetterReturnExpression(method);
|
||||
if (value == null) return null;
|
||||
PsiField field = getSimplyReturnedField(method, getGetterReturnExpression(method));
|
||||
if (field != null) {
|
||||
final PsiType returnType = method.getReturnType();
|
||||
if (returnType != null && field.getType().equalsToText(returnType.getCanonicalText())) {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiField getSimplyReturnedField(PsiMethod method, @Nullable PsiExpression value) {
|
||||
if (!(value instanceof PsiReferenceExpression)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final PsiReferenceExpression reference = (PsiReferenceExpression)value;
|
||||
final PsiExpression qualifier = reference.getQualifierExpression();
|
||||
if (qualifier instanceof PsiReferenceExpression) {
|
||||
final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)qualifier;
|
||||
final PsiElement target = referenceExpression.resolve();
|
||||
if (!(target instanceof PsiClass)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else if (qualifier != null && !(qualifier instanceof PsiThisExpression) && !(qualifier instanceof PsiSuperExpression)) {
|
||||
if (hasSubstantialQualifier(reference)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final PsiElement referent = reference.resolve();
|
||||
if (referent == null) {
|
||||
return null;
|
||||
}
|
||||
if (!(referent instanceof PsiField)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final PsiField field = (PsiField)referent;
|
||||
final PsiType fieldType = field.getType();
|
||||
final PsiType returnType = method.getReturnType();
|
||||
if (returnType == null) {
|
||||
return null;
|
||||
return InheritanceUtil.isInheritorOrSelf(method.getContainingClass(), field.getContainingClass(), true) ? field : null;
|
||||
}
|
||||
|
||||
private static boolean hasSubstantialQualifier(PsiReferenceExpression reference) {
|
||||
final PsiExpression qualifier = reference.getQualifierExpression();
|
||||
if (qualifier == null) return false;
|
||||
|
||||
if (qualifier instanceof PsiThisExpression || qualifier instanceof PsiSuperExpression) {
|
||||
return false;
|
||||
}
|
||||
if (!fieldType.equalsToText(returnType.getCanonicalText())) {
|
||||
return null;
|
||||
}
|
||||
final PsiClass fieldContainingClass = field.getContainingClass();
|
||||
final PsiClass methodContainingClass = method.getContainingClass();
|
||||
if (InheritanceUtil.isInheritorOrSelf(methodContainingClass, fieldContainingClass, true)) {
|
||||
return field;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
|
||||
if (qualifier instanceof PsiReferenceExpression) {
|
||||
return !(((PsiReferenceExpression)qualifier).resolve() instanceof PsiClass);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isSimpleGetter(PsiMethod method) {
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.String;
|
||||
|
||||
public class BrokenAlignment {
|
||||
|
||||
void main(Data data) {
|
||||
if (data.getText() != null) {
|
||||
System.out.println(data.getText().hashCode());
|
||||
}
|
||||
|
||||
data = new Data(null, null);
|
||||
System.out.println(<warning descr="Method invocation 'data.getText().hashCode()' may produce 'java.lang.NullPointerException'">data.getText().hashCode()</warning>);
|
||||
|
||||
if (data.inner() != null) {
|
||||
System.out.println(data.inner().hashCode());
|
||||
System.out.println(<warning descr="Method invocation 'data.inner().getText().hashCode()' may produce 'java.lang.NullPointerException'">data.inner().getText().hashCode()</warning>);
|
||||
/*
|
||||
if (data.inner() != null) {
|
||||
System.out.println(data.inner().hashCode());
|
||||
}
|
||||
*/
|
||||
|
||||
data = new Data(null, null);
|
||||
System.out.println(<warning descr="Method invocation 'data.inner().hashCode()' may produce 'java.lang.NullPointerException'">data.inner().hashCode()</warning>);
|
||||
}
|
||||
}
|
||||
|
||||
void main2(Data data) {
|
||||
if (data.inner() != null && data.inner().getText() != null) {
|
||||
System.out.println(data.inner().hashCode());
|
||||
System.out.println(data.inner().getText().hashCode());
|
||||
}
|
||||
}
|
||||
|
||||
void main3(Data data) {
|
||||
if (data.innerOverridden() != null) {
|
||||
System.out.println(data.innerOverridden().hashCode());
|
||||
}
|
||||
if (data.something() != null) {
|
||||
System.out.println(<warning descr="Method invocation 'data.something().hashCode()' may produce 'java.lang.NullPointerException'">data.something().hashCode()</warning>);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Data {
|
||||
@Nullable final String text;
|
||||
@Nullable final Data inner;
|
||||
|
||||
Data(@Nullable String text, Data inner) {
|
||||
this.text = text;
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Data inner() {
|
||||
return inner;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Data innerOverridden() {
|
||||
return inner;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String something() {
|
||||
return new String();
|
||||
}
|
||||
}
|
||||
|
||||
class DataImpl extends Data {
|
||||
DataImpl(@Nullable String text, Data inner) {
|
||||
super(text, inner);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Data innerOverridden() {
|
||||
return super.innerOverridden();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
public class BrokenAlignment {
|
||||
|
||||
void main(Data data) {
|
||||
if (data.text != null) {
|
||||
System.out.println(data.text.hashCode());
|
||||
}
|
||||
|
||||
data = new Data(null, null);
|
||||
System.out.println(<warning descr="Method invocation 'data.text.hashCode()' may produce 'java.lang.NullPointerException'">data.text.hashCode()</warning>);
|
||||
|
||||
if (data.inner != null) {
|
||||
System.out.println(data.inner.hashCode());
|
||||
System.out.println(<warning descr="Method invocation 'data.inner.text.hashCode()' may produce 'java.lang.NullPointerException'">data.inner.text.hashCode()</warning>);
|
||||
if (<warning descr="Condition 'data.inner != null' is always 'true'">data.inner != null</warning>) {
|
||||
System.out.println(data.inner.hashCode());
|
||||
}
|
||||
|
||||
data = new Data(null, null);
|
||||
System.out.println(<warning descr="Method invocation 'data.inner.hashCode()' may produce 'java.lang.NullPointerException'">data.inner.hashCode()</warning>);
|
||||
}
|
||||
}
|
||||
|
||||
void main2(Data data) {
|
||||
if (data.inner != null && data.inner.text != null) {
|
||||
System.out.println(data.inner.hashCode());
|
||||
System.out.println(data.inner.text.hashCode());
|
||||
}
|
||||
}
|
||||
|
||||
private static class Data {
|
||||
@Nullable public final String text;
|
||||
@Nullable public final Data inner;
|
||||
|
||||
Data(@Nullable String text, Data inner) {
|
||||
this.text = text;
|
||||
this.inner = inner;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,4 +78,7 @@ public class DataFlowInspectionFixtureTest extends JavaCodeInsightFixtureTestCas
|
||||
public void testFinalLoopVariableInstanceof() throws Throwable { doTest(); }
|
||||
public void testGreaterIsNotEquals() throws Throwable { doTest(); }
|
||||
|
||||
public void testChainedFinalFieldsDfa() throws Throwable { doTest(); }
|
||||
public void testChainedFinalFieldAccessorsDfa() throws Throwable { doTest(); }
|
||||
|
||||
}
|
||||
|
||||
-14
@@ -84,20 +84,6 @@ class DependencyContext {
|
||||
}
|
||||
}
|
||||
|
||||
public int getFilePath(final String path) {
|
||||
try {
|
||||
if (StringUtil.isEmpty(path)) {
|
||||
return myEmptyName;
|
||||
}
|
||||
final String _path = FileUtil.toSystemIndependentName(path);
|
||||
//return myEnumerator.enumerate(SystemInfo.isFileSystemCaseSensitive ? _path : _path.toLowerCase(Locale.US));
|
||||
return myEnumerator.enumerate(_path);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
try {
|
||||
myEnumerator.close();
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2000-2011 JetBrains s.r.o.
|
||||
*
|
||||
* 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 org.jetbrains.jps.builders.java.dependencyView;
|
||||
|
||||
import gnu.trove.TIntObjectProcedure;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
/**
|
||||
* Created by IntelliJ IDEA.
|
||||
* User: db
|
||||
* Date: 04.11.11
|
||||
* Time: 23:48
|
||||
* To change this template use File | Settings | File Templates.
|
||||
*/
|
||||
abstract class IntObjectMaplet<V> implements Streamable {
|
||||
abstract boolean containsKey(final int key);
|
||||
|
||||
abstract V get(final int key);
|
||||
|
||||
abstract void put(final int key, final V value);
|
||||
|
||||
abstract void putAll(IntObjectMaplet<V> m);
|
||||
|
||||
abstract void remove(final int key);
|
||||
|
||||
abstract void close();
|
||||
|
||||
abstract void forEachEntry(TIntObjectProcedure<V> proc);
|
||||
|
||||
abstract void flush(boolean memoryCachesOnly);
|
||||
|
||||
public void toStream(final DependencyContext context, final PrintStream stream) {
|
||||
final OrderProvider op = new OrderProvider(context);
|
||||
|
||||
forEachEntry(new TIntObjectProcedure<V>() {
|
||||
@Override
|
||||
public boolean execute(final int a, final V b) {
|
||||
op.register(a);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
final int[] keys = op.get();
|
||||
|
||||
for (final int a : keys) {
|
||||
final V b = get(a);
|
||||
|
||||
stream.print(" ");
|
||||
stream.print(context.getValue(a));
|
||||
stream.print(" -> ");
|
||||
|
||||
stream.print(b.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
package org.jetbrains.jps.builders.java.dependencyView;
|
||||
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.SLRUCache;
|
||||
import com.intellij.util.io.DataExternalizer;
|
||||
import com.intellij.util.io.IntInlineKeyDescriptor;
|
||||
import com.intellij.util.io.PersistentHashMap;
|
||||
import gnu.trove.TIntObjectProcedure;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author Eugene Zhuravlev
|
||||
* Date: 9/10/12
|
||||
*/
|
||||
public class IntObjectPersistentMaplet<V> extends IntObjectMaplet<V>{
|
||||
|
||||
private static final Object NULL_OBJ = new Object();
|
||||
private static final int CACHE_SIZE = 512;
|
||||
private final PersistentHashMap<Integer, V> myMap;
|
||||
private final SLRUCache<Integer, Object> myCache;
|
||||
|
||||
public IntObjectPersistentMaplet(final File file, final DataExternalizer<V> externalizer) {
|
||||
try {
|
||||
myMap = new PersistentHashMap<Integer, V>(file, new IntInlineKeyDescriptor(), externalizer);
|
||||
myCache = new SLRUCache<Integer, Object>(CACHE_SIZE, CACHE_SIZE) {
|
||||
@NotNull
|
||||
@Override
|
||||
public Object createValue(Integer key) {
|
||||
try {
|
||||
final V v1 = myMap.get(key);
|
||||
return v1 == null? NULL_OBJ : v1;
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(final int key) {
|
||||
try {
|
||||
return myMap.containsMapping(key);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(final int key) {
|
||||
final Object obj = myCache.get(key);
|
||||
return obj == NULL_OBJ? null : (V)obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(final int key, final V value) {
|
||||
try {
|
||||
myCache.remove(key);
|
||||
myMap.put(key, value);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(final IntObjectMaplet<V> m) {
|
||||
m.forEachEntry(new TIntObjectProcedure<V>() {
|
||||
@Override
|
||||
public boolean execute(int key, V value) {
|
||||
put(key, value);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(final int key) {
|
||||
try {
|
||||
myCache.remove(key);
|
||||
myMap.remove(key);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
myCache.clear();
|
||||
myMap.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void flush(boolean memoryCachesOnly) {
|
||||
if (memoryCachesOnly) {
|
||||
if (myMap.isDirty()) {
|
||||
myMap.dropMemoryCaches();
|
||||
}
|
||||
}
|
||||
else {
|
||||
myMap.force();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachEntry(final TIntObjectProcedure<V> proc) {
|
||||
try {
|
||||
myMap.processKeysWithExistingMapping(new Processor<Integer>() {
|
||||
@Override
|
||||
public boolean process(Integer key) {
|
||||
try {
|
||||
final V value = myMap.get(key);
|
||||
return value == null? proc.execute(key, null) : proc.execute(key, value);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package org.jetbrains.jps.builders.java.dependencyView;
|
||||
|
||||
import gnu.trove.TIntObjectHashMap;
|
||||
import gnu.trove.TIntObjectProcedure;
|
||||
|
||||
/**
|
||||
* @author Eugene Zhuravlev
|
||||
* Date: 9/10/12
|
||||
*/
|
||||
public class IntObjectTransientMaplet<V> extends IntObjectMaplet<V>{
|
||||
private final TIntObjectHashMap<V> myMap = new TIntObjectHashMap<V>();
|
||||
@Override
|
||||
boolean containsKey(int key) {
|
||||
return myMap.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
V get(int key) {
|
||||
return myMap.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
void put(int key, V value) {
|
||||
myMap.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
void putAll(IntObjectMaplet<V> m) {
|
||||
m.forEachEntry(new TIntObjectProcedure<V>() {
|
||||
@Override
|
||||
public boolean execute(int key, V value) {
|
||||
myMap.put(key, value);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
void remove(int key) {
|
||||
myMap.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
void close() {
|
||||
myMap.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
void forEachEntry(TIntObjectProcedure<V> proc) {
|
||||
myMap.forEachEntry(proc);
|
||||
}
|
||||
|
||||
@Override
|
||||
void flush(boolean memoryCachesOnly) {
|
||||
}
|
||||
}
|
||||
+91
-141
@@ -5,14 +5,12 @@ import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.util.io.IntInlineKeyDescriptor;
|
||||
import gnu.trove.TIntHashSet;
|
||||
import gnu.trove.TIntIntProcedure;
|
||||
import gnu.trove.TIntObjectProcedure;
|
||||
import gnu.trove.TIntProcedure;
|
||||
import gnu.trove.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.asm4.ClassReader;
|
||||
import org.jetbrains.asm4.Opcodes;
|
||||
import org.jetbrains.jps.incremental.storage.FileKeyDescriptor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
@@ -36,8 +34,6 @@ public class Mappings {
|
||||
private final static String CLASS_TO_SUBCLASSES = "classToSubclasses.tab";
|
||||
private final static String CLASS_TO_CLASS = "classToClass.tab";
|
||||
private final static String SOURCE_TO_CLASS = "sourceToClass.tab";
|
||||
private final static String SOURCE_TO_ANNOTATIONS = "sourceToAnnotations.tab";
|
||||
private final static String SOURCE_TO_USAGES = "sourceToUsages.tab";
|
||||
private final static String CLASS_TO_SOURCE = "classToSource.tab";
|
||||
private static final IntInlineKeyDescriptor INT_KEY_DESCRIPTOR = new IntInlineKeyDescriptor();
|
||||
private static final int DEFAULT_SET_CAPACITY = 32;
|
||||
@@ -54,7 +50,7 @@ public class Mappings {
|
||||
private boolean myIsRebuild = false;
|
||||
|
||||
private final TIntHashSet myChangedClasses;
|
||||
private final TIntHashSet myChangedFiles;
|
||||
private final THashSet<File> myChangedFiles;
|
||||
private final Set<ClassRepr> myDeletedClasses;
|
||||
private final Object myLock;
|
||||
private final File myRootDir;
|
||||
@@ -66,8 +62,8 @@ public class Mappings {
|
||||
|
||||
private IntIntMultiMaplet myClassToSubclasses;
|
||||
private IntIntMultiMaplet myClassToClassDependency;
|
||||
private IntObjectMultiMaplet<ClassRepr> mySourceFileToClasses;
|
||||
private IntIntMaplet myClassToSourceFile;
|
||||
private ObjectObjectMultiMaplet<File, ClassRepr> mySourceFileToClasses;
|
||||
private IntObjectMaplet<File> myClassToSourceFile;
|
||||
|
||||
private IntIntTransientMultiMaplet myRemovedSuperClasses;
|
||||
private IntIntTransientMultiMaplet myAddedSuperClasses;
|
||||
@@ -79,7 +75,7 @@ public class Mappings {
|
||||
myLock = base.myLock;
|
||||
myIsDelta = true;
|
||||
myChangedClasses = new TIntHashSet(DEFAULT_SET_CAPACITY, DEFAULT_SET_LOAD_FACTOR);
|
||||
myChangedFiles = new TIntHashSet(DEFAULT_SET_CAPACITY, DEFAULT_SET_LOAD_FACTOR);
|
||||
myChangedFiles = new THashSet(FileUtil.FILE_HASHING_STRATEGY);
|
||||
myDeletedClasses = new HashSet<ClassRepr>(DEFAULT_SET_CAPACITY, DEFAULT_SET_LOAD_FACTOR);
|
||||
myDeltaIsTransient = base.myDeltaIsTransient;
|
||||
myRootDir = new File(FileUtil.toSystemIndependentName(base.myRootDir.getAbsolutePath()) + File.separatorChar + "myDelta");
|
||||
@@ -115,8 +111,8 @@ public class Mappings {
|
||||
if (myIsDelta && myDeltaIsTransient) {
|
||||
myClassToSubclasses = new IntIntTransientMultiMaplet();
|
||||
myClassToClassDependency = new IntIntTransientMultiMaplet();
|
||||
mySourceFileToClasses = new IntObjectTransientMultiMaplet<ClassRepr>(ourClassSetConstructor);
|
||||
myClassToSourceFile = new IntIntTransientMaplet();
|
||||
mySourceFileToClasses = new ObjectObjectTransientMultiMaplet<File, ClassRepr>(FileUtil.FILE_HASHING_STRATEGY, ourClassSetConstructor);
|
||||
myClassToSourceFile = new IntObjectTransientMaplet<File>();
|
||||
}
|
||||
else {
|
||||
if (myIsDelta) {
|
||||
@@ -124,11 +120,11 @@ public class Mappings {
|
||||
}
|
||||
myClassToSubclasses = new IntIntPersistentMultiMaplet(DependencyContext.getTableFile(myRootDir, CLASS_TO_SUBCLASSES), INT_KEY_DESCRIPTOR);
|
||||
myClassToClassDependency = new IntIntPersistentMultiMaplet(DependencyContext.getTableFile(myRootDir, CLASS_TO_CLASS), INT_KEY_DESCRIPTOR);
|
||||
mySourceFileToClasses = new IntObjectPersistentMultiMaplet<ClassRepr>(
|
||||
DependencyContext.getTableFile(myRootDir, SOURCE_TO_CLASS), INT_KEY_DESCRIPTOR, ClassRepr.externalizer(myContext),
|
||||
mySourceFileToClasses = new ObjectObjectPersistentMultiMaplet<File, ClassRepr>(
|
||||
DependencyContext.getTableFile(myRootDir, SOURCE_TO_CLASS), new FileKeyDescriptor(), ClassRepr.externalizer(myContext),
|
||||
ourClassSetConstructor
|
||||
);
|
||||
myClassToSourceFile = new IntIntPersistentMaplet(DependencyContext.getTableFile(myRootDir, CLASS_TO_SOURCE), INT_KEY_DESCRIPTOR);
|
||||
myClassToSourceFile = new IntObjectPersistentMaplet<File>(DependencyContext.getTableFile(myRootDir, CLASS_TO_SOURCE), new FileKeyDescriptor());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,9 +142,8 @@ public class Mappings {
|
||||
private void compensateRemovedContent(final Collection<File> compiled) {
|
||||
if (compiled != null) {
|
||||
for (final File file : compiled) {
|
||||
final int fileName = myContext.getFilePath(file.getPath());
|
||||
if (!mySourceFileToClasses.containsKey(fileName)) {
|
||||
mySourceFileToClasses.put(fileName, new HashSet<ClassRepr>());
|
||||
if (!mySourceFileToClasses.containsKey(file)) {
|
||||
mySourceFileToClasses.put(file, new HashSet<ClassRepr>());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,9 +151,9 @@ public class Mappings {
|
||||
|
||||
@Nullable
|
||||
private ClassRepr getReprByName(final int name) {
|
||||
final int source = myClassToSourceFile.get(name);
|
||||
final File source = myClassToSourceFile.get(name);
|
||||
|
||||
if (source > 0) {
|
||||
if (source != null) {
|
||||
final Collection<ClassRepr> reprs = mySourceFileToClasses.get(source);
|
||||
|
||||
if (reprs != null) {
|
||||
@@ -493,8 +488,8 @@ public class Mappings {
|
||||
private void affectSubclasses(final int className, final Collection<File> affectedFiles, final Collection<UsageRepr.Usage> affectedUsages, final TIntHashSet dependants, final boolean usages) {
|
||||
debug("Affecting subclasses of class: ", className);
|
||||
|
||||
final int fileName = myClassToSourceFile.get(className);
|
||||
if (fileName <= 0) {
|
||||
final File fileName = myClassToSourceFile.get(className);
|
||||
if (fileName == null) {
|
||||
debug("No source file detected for class ", className);
|
||||
debug("End of affectSubclasses");
|
||||
return;
|
||||
@@ -516,7 +511,7 @@ public class Mappings {
|
||||
if (depClasses != null) {
|
||||
addAll(dependants, depClasses);
|
||||
}
|
||||
affectedFiles.add(new File(myContext.getValue(fileName)));
|
||||
affectedFiles.add(fileName);
|
||||
|
||||
final TIntHashSet directSubclasses = myClassToSubclasses.get(className);
|
||||
if (directSubclasses != null) {
|
||||
@@ -631,18 +626,17 @@ public class Mappings {
|
||||
}
|
||||
|
||||
private void affectAll(final int className, final Collection<File> affectedFiles, @Nullable final DependentFilesFilter filter) {
|
||||
final int sourceFile = myClassToSourceFile.get(className);
|
||||
if (sourceFile > 0) {
|
||||
final File sourceFile = myClassToSourceFile.get(className);
|
||||
if (sourceFile != null) {
|
||||
final TIntHashSet dependants = myClassToClassDependency.get(className);
|
||||
if (dependants != null) {
|
||||
dependants.forEach(new TIntProcedure() {
|
||||
@Override
|
||||
public boolean execute(int depClass) {
|
||||
final int depFile = myClassToSourceFile.get(depClass);
|
||||
if (depFile > 0 && depFile != sourceFile) {
|
||||
final File theFile = new File(myContext.getValue(depFile));
|
||||
if (filter == null || filter.accept(theFile)) {
|
||||
affectedFiles.add(theFile);
|
||||
final File depFile = myClassToSourceFile.get(depClass);
|
||||
if (depFile != null && !FileUtil.filesEqual(depFile, sourceFile)) {
|
||||
if (filter == null || filter.accept(depFile)) {
|
||||
affectedFiles.add(depFile);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -702,26 +696,17 @@ public class Mappings {
|
||||
debug("Root class: ", owner);
|
||||
|
||||
final TIntHashSet propagated = self.propagateFieldAccess(isField ? member.name : myEmptyName, owner);
|
||||
final TIntHashSet fileNames = new TIntHashSet(propagated.size());
|
||||
propagated.forEach(new TIntProcedure() {
|
||||
@Override
|
||||
public boolean execute(int className) {
|
||||
final int fileName = myClassToSourceFile.get(className);
|
||||
if (fileName > 0) {
|
||||
fileNames.add(fileName);
|
||||
final File fileName = myClassToSourceFile.get(className);
|
||||
if (fileName != null) {
|
||||
debug("Adding ", fileName);
|
||||
affectedFiles.add(fileName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
fileNames.forEach(new TIntProcedure() {
|
||||
@Override
|
||||
public boolean execute(int file) {
|
||||
final String fileName = myContext.getValue(file);
|
||||
debug("Adding ", fileName);
|
||||
affectedFiles.add(new File(fileName));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
final String packageName = ClassRepr.getPackageName(myContext.getValue(isField ? owner : member.name));
|
||||
@@ -730,24 +715,14 @@ public class Mappings {
|
||||
debug("Package name: ", packageName);
|
||||
|
||||
// Package-local branch
|
||||
final TIntHashSet fileNames = new TIntHashSet(DEFAULT_SET_CAPACITY, DEFAULT_SET_LOAD_FACTOR);
|
||||
myClassToSourceFile.forEachEntry(new TIntIntProcedure() {
|
||||
myClassToSourceFile.forEachEntry(new TIntObjectProcedure<File>() {
|
||||
@Override
|
||||
public boolean execute(int className, int fileName) {
|
||||
public boolean execute(int className, File fileName) {
|
||||
if (ClassRepr.getPackageName(myContext.getValue(className)).equals(packageName)) {
|
||||
fileNames.add(fileName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
fileNames.forEach(new TIntProcedure() {
|
||||
@Override
|
||||
public boolean execute(int fileName) {
|
||||
final String f = myContext.getValue(fileName);
|
||||
final File file = new File(f);
|
||||
if (filter == null || filter.accept(file)) {
|
||||
debug("Adding: ", f);
|
||||
affectedFiles.add(file);
|
||||
if (filter == null || filter.accept(fileName)) {
|
||||
debug("Adding: ", fileName);
|
||||
affectedFiles.add(fileName);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -859,10 +834,10 @@ public class Mappings {
|
||||
}
|
||||
|
||||
private class FileClasses {
|
||||
final int myFileName;
|
||||
final File myFileName;
|
||||
final Set<ClassRepr> myFileClasses;
|
||||
|
||||
FileClasses(int fileName, Collection<ClassRepr> fileClasses) {
|
||||
FileClasses(File fileName, Collection<ClassRepr> fileClasses) {
|
||||
this.myFileName = fileName;
|
||||
this.myFileClasses = new HashSet<ClassRepr>(fileClasses);
|
||||
}
|
||||
@@ -949,7 +924,7 @@ public class Mappings {
|
||||
|
||||
if (removed != null) {
|
||||
for (final String file : removed) {
|
||||
final Collection<ClassRepr> classes = mySourceFileToClasses.get(myContext.getFilePath(file));
|
||||
final Collection<ClassRepr> classes = mySourceFileToClasses.get(new File(file));
|
||||
|
||||
if (classes != null) {
|
||||
for (ClassRepr c : classes) {
|
||||
@@ -972,7 +947,6 @@ public class Mappings {
|
||||
debug("Class is annotation, skipping method analysis");
|
||||
return;
|
||||
}
|
||||
final TIntHashSet affectedFiles = new TIntHashSet(DEFAULT_SET_CAPACITY, DEFAULT_SET_LOAD_FACTOR);
|
||||
Ref<ClassRepr> oldItRef = null;
|
||||
for (final MethodRepr m : added) {
|
||||
debug("Method: ", m.name);
|
||||
@@ -1037,10 +1011,10 @@ public class Mappings {
|
||||
if (overrides.satisfy(method) && isInheritor) {
|
||||
debug("Current method overrides that found");
|
||||
|
||||
final int file = myClassToSourceFile.get(methodClass.name);
|
||||
final File file = myClassToSourceFile.get(methodClass.name);
|
||||
|
||||
if (file > 0) {
|
||||
affectedFiles.add(file);
|
||||
if (file != null) {
|
||||
myAffectedFiles.add(file);
|
||||
debug("Affecting file ", file);
|
||||
}
|
||||
}
|
||||
@@ -1074,11 +1048,11 @@ public class Mappings {
|
||||
public boolean execute(int subClass) {
|
||||
final ClassRepr r = myFuture.reprByName(subClass);
|
||||
if (r != null) {
|
||||
final int sourceFileName = myClassToSourceFile.get(subClass);
|
||||
if (sourceFileName > 0) {
|
||||
final File sourceFileName = myClassToSourceFile.get(subClass);
|
||||
if (sourceFileName != null) {
|
||||
final int outerClass = r.getOuterClassName();
|
||||
if (myFuture.isMethodVisible(outerClass, m)) {
|
||||
affectedFiles.add(sourceFileName);
|
||||
myAffectedFiles.add(sourceFileName);
|
||||
debug("Affecting file due to local overriding: ", sourceFileName);
|
||||
}
|
||||
}
|
||||
@@ -1089,13 +1063,6 @@ public class Mappings {
|
||||
}
|
||||
}
|
||||
}
|
||||
affectedFiles.forEach(new TIntProcedure() {
|
||||
@Override
|
||||
public boolean execute(int file) {
|
||||
myAffectedFiles.add(new File(myContext.getValue(file)));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
debug("End of added methods processing");
|
||||
}
|
||||
|
||||
@@ -1105,7 +1072,6 @@ public class Mappings {
|
||||
return;
|
||||
}
|
||||
debug("Processing removed methods:");
|
||||
final TIntHashSet affectedFiles = new TIntHashSet(DEFAULT_SET_CAPACITY, DEFAULT_SET_LOAD_FACTOR);
|
||||
for (final MethodRepr m : removed) {
|
||||
debug("Method ", m.name);
|
||||
|
||||
@@ -1140,8 +1106,8 @@ public class Mappings {
|
||||
myFuture.addOverridingMethods(m, it, MethodRepr.equalByJavaRules(m), overridingMethods);
|
||||
|
||||
for (final Pair<MethodRepr, ClassRepr> p : overridingMethods) {
|
||||
final int fName = myClassToSourceFile.get(p.second.name);
|
||||
affectedFiles.add(fName);
|
||||
final File fName = myClassToSourceFile.get(p.second.name);
|
||||
myAffectedFiles.add(fName);
|
||||
debug("Affecting file by overriding: ", fName);
|
||||
}
|
||||
|
||||
@@ -1181,10 +1147,10 @@ public class Mappings {
|
||||
}
|
||||
|
||||
if (allAbstract && visited) {
|
||||
final int source = myClassToSourceFile.get(p);
|
||||
final File source = myClassToSourceFile.get(p);
|
||||
|
||||
if (source > 0) {
|
||||
affectedFiles.add(source);
|
||||
if (source != null) {
|
||||
myAffectedFiles.add(source);
|
||||
debug("Removed method is not abstract & overrides some abstract method which is not then over-overriden in subclass ", p);
|
||||
debug("Affecting subclass source file ", source);
|
||||
}
|
||||
@@ -1196,14 +1162,6 @@ public class Mappings {
|
||||
});
|
||||
}
|
||||
}
|
||||
affectedFiles.forEach(new TIntProcedure() {
|
||||
@Override
|
||||
public boolean execute(int file) {
|
||||
final String f = myContext.getValue(file);
|
||||
myAffectedFiles.add(new File(f));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
debug("End of removed methods processing");
|
||||
}
|
||||
|
||||
@@ -1264,10 +1222,9 @@ public class Mappings {
|
||||
final ClassRepr aClass = p.getSecond();
|
||||
|
||||
if (aClass != MOCK_CLASS) {
|
||||
final int fileName = myClassToSourceFile.get(aClass.name);
|
||||
|
||||
if (fileName > 0) {
|
||||
myAffectedFiles.add(new File(myContext.getValue(fileName)));
|
||||
final File fileName = myClassToSourceFile.get(aClass.name);
|
||||
if (fileName != null) {
|
||||
myAffectedFiles.add(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1340,17 +1297,17 @@ public class Mappings {
|
||||
public boolean execute(int subClass) {
|
||||
final ClassRepr r = myFuture.reprByName(subClass);
|
||||
if (r != null) {
|
||||
final int sourceFileName = myClassToSourceFile.get(subClass);
|
||||
if (sourceFileName > 0) {
|
||||
final File sourceFileName = myClassToSourceFile.get(subClass);
|
||||
if (sourceFileName != null) {
|
||||
if (r.isLocal()) {
|
||||
debug("Affecting local subclass (introduced field can potentially hide surrounding method parameters/local variables): ", sourceFileName);
|
||||
myAffectedFiles.add(new File(myContext.getValue(sourceFileName)));
|
||||
myAffectedFiles.add(sourceFileName);
|
||||
}
|
||||
else {
|
||||
final int outerClass = r.getOuterClassName();
|
||||
if (!isEmpty(outerClass) && myFuture.isFieldVisible(outerClass, f)) {
|
||||
debug("Affecting inner subclass (introduced field can potentially hide surrounding class fields): ", sourceFileName);
|
||||
myAffectedFiles.add(new File(myContext.getValue(sourceFileName)));
|
||||
myAffectedFiles.add(sourceFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1706,9 +1663,9 @@ public class Mappings {
|
||||
for (final ClassRepr c : removed) {
|
||||
myDelta.addDeletedClass(c);
|
||||
|
||||
final int fileName = myClassToSourceFile.get(c.name);
|
||||
final File fileName = myClassToSourceFile.get(c.name);
|
||||
|
||||
if (fileName > 0) {
|
||||
if (fileName != null) {
|
||||
myDelta.myChangedFiles.add(fileName);
|
||||
}
|
||||
|
||||
@@ -1740,25 +1697,15 @@ public class Mappings {
|
||||
final TIntHashSet depClasses = myClassToClassDependency.get(c.name);
|
||||
|
||||
if (depClasses != null) {
|
||||
final TIntHashSet fileNames = new TIntHashSet(DEFAULT_SET_CAPACITY, DEFAULT_SET_LOAD_FACTOR);
|
||||
depClasses.forEach(new TIntProcedure() {
|
||||
@Override
|
||||
public boolean execute(int depClass) {
|
||||
final int fName = myClassToSourceFile.get(depClass);
|
||||
if (fName > 0) {
|
||||
fileNames.add(fName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
fileNames.forEach(new TIntProcedure() {
|
||||
@Override
|
||||
public boolean execute(int fName) {
|
||||
final String f = myContext.getValue(fName);
|
||||
final File theFile = new File(f);
|
||||
if (myFilter == null || myFilter.accept(theFile)) {
|
||||
debug("Adding dependent file ", f);
|
||||
myAffectedFiles.add(theFile);
|
||||
final File fName = myClassToSourceFile.get(depClass);
|
||||
if (fName != null) {
|
||||
if (myFilter == null || myFilter.accept(fName)) {
|
||||
debug("Adding dependent file ", fName);
|
||||
myAffectedFiles.add(fName);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1776,12 +1723,11 @@ public class Mappings {
|
||||
state.myDependants.forEach(new TIntProcedure() {
|
||||
@Override
|
||||
public boolean execute(final int depClass) {
|
||||
final int depFile = myClassToSourceFile.get(depClass);
|
||||
final File depFile = myClassToSourceFile.get(depClass);
|
||||
|
||||
if (depFile > 0) {
|
||||
final File theFile = new File(myContext.getValue(depFile));
|
||||
if (depFile != null) {
|
||||
|
||||
if (myAffectedFiles.contains(theFile) || myCompiledFiles.contains(theFile)) {
|
||||
if (myAffectedFiles.contains(depFile) || myCompiledFiles.contains(depFile)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1804,7 +1750,7 @@ public class Mappings {
|
||||
for (final UsageRepr.AnnotationUsage query : state.myAnnotationQuery) {
|
||||
if (query.satisfies(usage)) {
|
||||
debug("Added file due to annotation query");
|
||||
myAffectedFiles.add(theFile);
|
||||
myAffectedFiles.add(depFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1815,14 +1761,14 @@ public class Mappings {
|
||||
|
||||
if (constraint == null) {
|
||||
debug("Added file with no constraints");
|
||||
myAffectedFiles.add(theFile);
|
||||
myAffectedFiles.add(depFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if (constraint.checkResidence(depClass)) {
|
||||
debug("Added file with satisfied constraint");
|
||||
myAffectedFiles.add(theFile);
|
||||
myAffectedFiles.add(depFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1850,16 +1796,16 @@ public class Mappings {
|
||||
processDisappearedClasses();
|
||||
|
||||
final List<FileClasses> newClasses = new ArrayList<FileClasses>();
|
||||
myDelta.mySourceFileToClasses.forEachEntry(new TIntObjectProcedure<Collection<ClassRepr>>() {
|
||||
myDelta.mySourceFileToClasses.forEachEntry(new TObjectObjectProcedure<File, Collection<ClassRepr>>() {
|
||||
@Override
|
||||
public boolean execute(int fileName, Collection<ClassRepr> classes) {
|
||||
public boolean execute(File fileName, Collection<ClassRepr> classes) {
|
||||
newClasses.add(new FileClasses(fileName, classes));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
for (final FileClasses compiledFile : newClasses) {
|
||||
final int fileName = compiledFile.myFileName;
|
||||
final File fileName = compiledFile.myFileName;
|
||||
final Set<ClassRepr> classes = compiledFile.myFileClasses;
|
||||
final Set<ClassRepr> pastClasses = (Set<ClassRepr>)mySourceFileToClasses.get(fileName);
|
||||
final DiffState state = new DiffState(Difference.make(pastClasses, classes));
|
||||
@@ -1965,7 +1911,7 @@ public class Mappings {
|
||||
|
||||
if (removed != null) {
|
||||
for (final String file : removed) {
|
||||
final int fileName = myContext.getFilePath(file);
|
||||
final File fileName = new File(file);
|
||||
final Set<ClassRepr> fileClasses = (Set<ClassRepr>)mySourceFileToClasses.get(fileName);
|
||||
|
||||
if (fileClasses != null) {
|
||||
@@ -2019,8 +1965,8 @@ public class Mappings {
|
||||
delta.getChangedClasses().forEach(new TIntProcedure() {
|
||||
@Override
|
||||
public boolean execute(final int className) {
|
||||
final int sourceFile = delta.myClassToSourceFile.get(className);
|
||||
if (sourceFile > 0) {
|
||||
final File sourceFile = delta.myClassToSourceFile.get(className);
|
||||
if (sourceFile != null) {
|
||||
myClassToSourceFile.put(className, sourceFile);
|
||||
}
|
||||
else {
|
||||
@@ -2033,9 +1979,9 @@ public class Mappings {
|
||||
}
|
||||
});
|
||||
|
||||
delta.getChangedFiles().forEach(new TIntProcedure() {
|
||||
delta.getChangedFiles().forEach(new TObjectProcedure<File>() {
|
||||
@Override
|
||||
public boolean execute(final int fileName) {
|
||||
public boolean execute(final File fileName) {
|
||||
final Collection<ClassRepr> classes = delta.mySourceFileToClasses.get(fileName);
|
||||
mySourceFileToClasses.replace(fileName, classes);
|
||||
return true;
|
||||
@@ -2101,16 +2047,16 @@ public class Mappings {
|
||||
return new Callbacks.Backend() {
|
||||
public void associate(final String classFileName, final String sourceFileName, final ClassReader cr) {
|
||||
synchronized (myLock) {
|
||||
final int classFileNameS = myContext.getFilePath(classFileName);
|
||||
final int classFileNameS = myContext.get(classFileName);
|
||||
final Pair<ClassRepr, Set<UsageRepr.Usage>> result = new ClassfileAnalyzer(myContext).analyze(classFileNameS, cr);
|
||||
final ClassRepr repr = result.first;
|
||||
if (repr != null) {
|
||||
final Set<UsageRepr.Usage> localUsages = result.second;
|
||||
final int sourceFileNameS = myContext.getFilePath(sourceFileName);
|
||||
final File sourceFile = new File(sourceFileName);
|
||||
final int className = repr.name;
|
||||
|
||||
myClassToSourceFile.put(className, sourceFileNameS);
|
||||
mySourceFileToClasses.put(sourceFileNameS, repr);
|
||||
myClassToSourceFile.put(className, sourceFile);
|
||||
mySourceFileToClasses.put(sourceFile, repr);
|
||||
|
||||
for (final int s : repr.getSupers()) {
|
||||
myClassToSubclasses.put(s, className);
|
||||
@@ -2148,8 +2094,8 @@ public class Mappings {
|
||||
myPostPasses.offer(new Runnable() {
|
||||
public void run() {
|
||||
final int rootClassName = myContext.get(className.replace(".", "/"));
|
||||
final int fileName = myClassToSourceFile.get(rootClassName);
|
||||
final ClassRepr repr = fileName > 0? getReprByName(rootClassName) : null;
|
||||
final File fileName = myClassToSourceFile.get(rootClassName);
|
||||
final ClassRepr repr = fileName != null? getReprByName(rootClassName) : null;
|
||||
|
||||
for (final String i : allImports) {
|
||||
final int iname = myContext.get(i.replace(".", "/"));
|
||||
@@ -2168,7 +2114,7 @@ public class Mappings {
|
||||
@Nullable
|
||||
public Set<ClassRepr> getClasses(final String sourceFileName) {
|
||||
synchronized (myLock) {
|
||||
return (Set<ClassRepr>)mySourceFileToClasses.get(myContext.getFilePath(sourceFileName));
|
||||
return (Set<ClassRepr>)mySourceFileToClasses.get(new File(sourceFileName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2272,9 +2218,9 @@ public class Mappings {
|
||||
assert (myChangedClasses != null && myChangedFiles != null);
|
||||
myChangedClasses.add(it);
|
||||
|
||||
final int file = myClassToSourceFile.get(it);
|
||||
final File file = myClassToSourceFile.get(it);
|
||||
|
||||
if (file > 0) {
|
||||
if (file != null) {
|
||||
myChangedFiles.add(file);
|
||||
}
|
||||
}
|
||||
@@ -2288,7 +2234,7 @@ public class Mappings {
|
||||
return myChangedClasses;
|
||||
}
|
||||
|
||||
private TIntHashSet getChangedFiles() {
|
||||
private THashSet<File> getChangedFiles() {
|
||||
return myChangedFiles;
|
||||
}
|
||||
|
||||
@@ -2300,6 +2246,10 @@ public class Mappings {
|
||||
myDebugS.debug(comment, s);
|
||||
}
|
||||
|
||||
private void debug(final String comment, final File f) {
|
||||
debug(comment, f.getPath());
|
||||
}
|
||||
|
||||
private void debug(final String comment, final String s) {
|
||||
myDebugS.debug(comment, s);
|
||||
}
|
||||
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2000-2011 JetBrains s.r.o.
|
||||
*
|
||||
* 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 org.jetbrains.jps.builders.java.dependencyView;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import gnu.trove.TObjectObjectProcedure;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.PrintStream;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Created by IntelliJ IDEA.
|
||||
* User: db
|
||||
* Date: 03.11.11
|
||||
* Time: 21:01
|
||||
* To change this template use File | Settings | File Templates.
|
||||
*/
|
||||
abstract class ObjectObjectMultiMaplet<K, V extends Streamable> implements Streamable {
|
||||
abstract boolean containsKey(final K key);
|
||||
|
||||
abstract Collection<V> get(final K key);
|
||||
|
||||
abstract void put(final K key, final V value);
|
||||
|
||||
abstract void put(final K key, final Collection<V> value);
|
||||
|
||||
abstract void replace(final K key, final Collection<V> value);
|
||||
|
||||
abstract void putAll(ObjectObjectMultiMaplet<K, V> m);
|
||||
|
||||
abstract void replaceAll(ObjectObjectMultiMaplet<K, V> m);
|
||||
|
||||
abstract void remove(final K key);
|
||||
|
||||
abstract void removeFrom(final K key, final V value);
|
||||
|
||||
abstract void removeAll(final K key, final Collection<V> value);
|
||||
|
||||
abstract void close();
|
||||
|
||||
abstract void forEachEntry(TObjectObjectProcedure<K, Collection<V>> procedure);
|
||||
|
||||
abstract void flush(boolean memoryCachesOnly);
|
||||
|
||||
public void toStream(final DependencyContext context, final PrintStream stream) {
|
||||
|
||||
final List<Pair<K, String>> keys = new ArrayList<Pair<K, String>>();
|
||||
forEachEntry(new TObjectObjectProcedure<K, Collection<V>>() {
|
||||
@Override
|
||||
public boolean execute(final K a, final Collection<V> b) {
|
||||
// on case-insensitive file systems save paths in normalized (lowercase) format in order to make tests run deterministically
|
||||
final String keyStr = a instanceof File && !SystemInfo.isFileSystemCaseSensitive?
|
||||
((File)a).getPath().toLowerCase(Locale.US) :
|
||||
a.toString();
|
||||
keys.add(new Pair<K, String>(a, keyStr));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
Collections.sort(keys, new Comparator<Pair<K, String>>() {
|
||||
@Override
|
||||
public int compare(Pair<K, String> o1, Pair<K, String> o2) {
|
||||
return o1.second.compareTo(o2.second);
|
||||
}
|
||||
});
|
||||
|
||||
for (final Pair<K, String> a: keys) {
|
||||
final Collection<V> b = get(a.first);
|
||||
|
||||
stream.print(" Key: ");
|
||||
stream.println(a.second);
|
||||
stream.println(" Values:");
|
||||
|
||||
final List<String> list = new LinkedList<String>();
|
||||
|
||||
for (final V value : b) {
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
final PrintStream s = new PrintStream(baos);
|
||||
|
||||
value.toStream(context, s);
|
||||
|
||||
list.add(baos.toString());
|
||||
}
|
||||
|
||||
Collections.sort(list);
|
||||
|
||||
for (final String l : list) {
|
||||
stream.print(l);
|
||||
}
|
||||
|
||||
stream.println(" End Of Values");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+248
@@ -0,0 +1,248 @@
|
||||
package org.jetbrains.jps.builders.java.dependencyView;
|
||||
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.SLRUCache;
|
||||
import com.intellij.util.io.DataExternalizer;
|
||||
import com.intellij.util.io.KeyDescriptor;
|
||||
import com.intellij.util.io.PersistentHashMap;
|
||||
import gnu.trove.TObjectObjectProcedure;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author Eugene Zhuravlev
|
||||
* Date: 9/10/12
|
||||
*/
|
||||
public class ObjectObjectPersistentMultiMaplet<K, V extends Streamable> extends ObjectObjectMultiMaplet<K, V>{
|
||||
private static final Collection NULL_COLLECTION = Collections.emptySet();
|
||||
private static final int CACHE_SIZE = 128;
|
||||
private final PersistentHashMap<K, Collection<V>> myMap;
|
||||
private final DataExternalizer<V> myValueExternalizer;
|
||||
private final SLRUCache<K, Collection> myCache;
|
||||
|
||||
public ObjectObjectPersistentMultiMaplet(final File file,
|
||||
final KeyDescriptor<K> keyExternalizer,
|
||||
final DataExternalizer<V> valueExternalizer,
|
||||
final CollectionFactory<V> collectionFactory) throws IOException {
|
||||
myValueExternalizer = valueExternalizer;
|
||||
myMap = new PersistentHashMap<K, Collection<V>>(file, keyExternalizer, new CollectionDataExternalizer<V>(valueExternalizer, collectionFactory));
|
||||
myCache = new SLRUCache<K, Collection>(CACHE_SIZE, CACHE_SIZE) {
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection createValue(K key) {
|
||||
try {
|
||||
final Collection<V> collection = myMap.get(key);
|
||||
return collection == null? NULL_COLLECTION : collection;
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean containsKey(final K key) {
|
||||
try {
|
||||
return myMap.containsMapping(key);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> get(final K key) {
|
||||
final Collection<V> collection = myCache.get(key);
|
||||
return collection == NULL_COLLECTION? null : collection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replace(K key, Collection<V> value) {
|
||||
try {
|
||||
myCache.remove(key);
|
||||
if (value == null || value.isEmpty()) {
|
||||
myMap.remove(key);
|
||||
}
|
||||
else {
|
||||
myMap.put(key, value);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(final K key, final Collection<V> value) {
|
||||
try {
|
||||
myCache.remove(key);
|
||||
myMap.appendData(key, new PersistentHashMap.ValueDataAppender() {
|
||||
public void append(DataOutput out) throws IOException {
|
||||
for (V v : value) {
|
||||
myValueExternalizer.save(out, v);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(final K key, final V value) {
|
||||
put(key, Collections.singleton(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAll(K key, Collection<V> values) {
|
||||
try {
|
||||
final Collection collection = myCache.get(key);
|
||||
|
||||
if (collection != NULL_COLLECTION) {
|
||||
if (collection.removeAll(values)) {
|
||||
myCache.remove(key);
|
||||
if (collection.isEmpty()) {
|
||||
myMap.remove(key);
|
||||
}
|
||||
else {
|
||||
myMap.put(key, (Collection<V>)collection);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFrom(final K key, final V value) {
|
||||
try {
|
||||
final Collection collection = myCache.get(key);
|
||||
|
||||
if (collection != NULL_COLLECTION) {
|
||||
if (collection.remove(value)) {
|
||||
myCache.remove(key);
|
||||
if (collection.isEmpty()) {
|
||||
myMap.remove(key);
|
||||
}
|
||||
else {
|
||||
myMap.put(key, (Collection<V>)collection);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(final K key) {
|
||||
try {
|
||||
myCache.remove(key);
|
||||
myMap.remove(key);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(ObjectObjectMultiMaplet<K, V> m) {
|
||||
m.forEachEntry(new TObjectObjectProcedure<K, Collection<V>>() {
|
||||
@Override
|
||||
public boolean execute(K key, Collection<V> value) {
|
||||
put(key, value);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceAll(ObjectObjectMultiMaplet<K, V> m) {
|
||||
m.forEachEntry(new TObjectObjectProcedure<K, Collection<V>>() {
|
||||
@Override
|
||||
public boolean execute(K key, Collection<V> value) {
|
||||
replace(key, value);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
myCache.clear();
|
||||
myMap.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void flush(boolean memoryCachesOnly) {
|
||||
if (memoryCachesOnly) {
|
||||
if (myMap.isDirty()) {
|
||||
myMap.dropMemoryCaches();
|
||||
}
|
||||
}
|
||||
else {
|
||||
myMap.force();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachEntry(final TObjectObjectProcedure<K, Collection<V>> procedure) {
|
||||
try {
|
||||
myMap.processKeysWithExistingMapping(new Processor<K>() {
|
||||
@Override
|
||||
public boolean process(K key) {
|
||||
try {
|
||||
return procedure.execute(key, myMap.get(key));
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static class CollectionDataExternalizer<V> implements DataExternalizer<Collection<V>> {
|
||||
private final DataExternalizer<V> myElementExternalizer;
|
||||
private final CollectionFactory<V> myCollectionFactory;
|
||||
|
||||
public CollectionDataExternalizer(DataExternalizer<V> elementExternalizer,
|
||||
CollectionFactory<V> collectionFactory) {
|
||||
myElementExternalizer = elementExternalizer;
|
||||
myCollectionFactory = collectionFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(final DataOutput out, final Collection<V> value) throws IOException {
|
||||
for (V x : value) {
|
||||
myElementExternalizer.save(out, x);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> read(final DataInput in) throws IOException {
|
||||
final Collection<V> result = myCollectionFactory.create();
|
||||
final DataInputStream stream = (DataInputStream)in;
|
||||
while (stream.available() > 0) {
|
||||
result.add(myElementExternalizer.read(in));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
package org.jetbrains.jps.builders.java.dependencyView;
|
||||
|
||||
import gnu.trove.THashMap;
|
||||
import gnu.trove.TObjectHashingStrategy;
|
||||
import gnu.trove.TObjectObjectProcedure;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author Eugene Zhuravlev
|
||||
* Date: 9/10/12
|
||||
*/
|
||||
public class ObjectObjectTransientMultiMaplet<K, V extends Streamable> extends ObjectObjectMultiMaplet<K, V>{
|
||||
|
||||
private final THashMap<K, Collection<V>> myMap;
|
||||
private final CollectionFactory<V> myCollectionFactory;
|
||||
|
||||
public ObjectObjectTransientMultiMaplet(TObjectHashingStrategy<K> hashingStrategy, CollectionFactory<V> collectionFactory) {
|
||||
myMap = new THashMap<K, Collection<V>>(hashingStrategy);
|
||||
myCollectionFactory = collectionFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(final K key) {
|
||||
return myMap.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> get(final K key) {
|
||||
return myMap.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(ObjectObjectMultiMaplet<K, V> m) {
|
||||
m.forEachEntry(new TObjectObjectProcedure<K, Collection<V>>() {
|
||||
@Override
|
||||
public boolean execute(K key, Collection<V> value) {
|
||||
put(key, value);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(final K key, final Collection<V> value) {
|
||||
final Collection<V> x = myMap.get(key);
|
||||
if (x == null) {
|
||||
myMap.put(key, value);
|
||||
}
|
||||
else {
|
||||
x.addAll(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replace(K key, Collection<V> value) {
|
||||
if (value == null || value.isEmpty()) {
|
||||
myMap.remove(key);
|
||||
}
|
||||
else {
|
||||
myMap.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(final K key, final V value) {
|
||||
final Collection<V> collection = myMap.get(key);
|
||||
if (collection == null) {
|
||||
final Collection<V> x = myCollectionFactory.create();
|
||||
x.add(value);
|
||||
myMap.put(key, x);
|
||||
}
|
||||
else {
|
||||
collection.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFrom(final K key, final V value) {
|
||||
final Collection<V> collection = myMap.get(key);
|
||||
if (collection != null) {
|
||||
if (collection.remove(value)) {
|
||||
if (collection.isEmpty()) {
|
||||
myMap.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAll(K key, Collection<V> values) {
|
||||
final Collection<V> collection = myMap.get(key);
|
||||
if (collection != null) {
|
||||
if (collection.removeAll(values)) {
|
||||
if (collection.isEmpty()) {
|
||||
myMap.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(final K key) {
|
||||
myMap.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceAll(ObjectObjectMultiMaplet<K, V> m) {
|
||||
m.forEachEntry(new TObjectObjectProcedure<K, Collection<V>>() {
|
||||
@Override
|
||||
public boolean execute(K key, Collection<V> value) {
|
||||
replace(key, value);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachEntry(TObjectObjectProcedure<K, Collection<V>> procedure) {
|
||||
myMap.forEachEntry(procedure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(){
|
||||
myMap.clear(); // free memory
|
||||
}
|
||||
|
||||
public void flush(boolean memoryCachesOnly) {
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import java.util.Map;
|
||||
* Date: 10/7/11
|
||||
*/
|
||||
public class BuildDataManager implements StorageOwner {
|
||||
private static final int VERSION = 9;
|
||||
private static final int VERSION = 10;
|
||||
private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.storage.BuildDataManager");
|
||||
private static final String SRC_TO_OUTPUTS_STORAGE = "src-out";
|
||||
private static final String SRC_TO_FORM_STORAGE = "src-form";
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.jetbrains.jps.incremental.storage;
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.util.io.IOUtil;
|
||||
import com.intellij.util.io.KeyDescriptor;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author Eugene Zhuravlev
|
||||
* Date: 9/10/12
|
||||
*/
|
||||
public final class FileKeyDescriptor implements KeyDescriptor<File> {
|
||||
private final byte[] buffer = IOUtil.allocReadWriteUTFBuffer();
|
||||
|
||||
public void save(DataOutput out, File value) throws IOException {
|
||||
IOUtil.writeUTFFast(buffer, out, value.getPath());
|
||||
}
|
||||
|
||||
public File read(DataInput in) throws IOException {
|
||||
return new File(IOUtil.readUTFFast(buffer, in));
|
||||
}
|
||||
|
||||
public int getHashCode(File value) {
|
||||
return FileUtil.fileHashCode(value);
|
||||
}
|
||||
|
||||
public boolean isEqual(File val1, File val2) {
|
||||
return FileUtil.filesEqual(val1, val2);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
package org.jetbrains.jps.incremental.storage;
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.util.io.DataExternalizer;
|
||||
import com.intellij.util.io.IOUtil;
|
||||
import com.intellij.util.io.KeyDescriptor;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
@@ -45,26 +42,6 @@ public class TimestampStorage extends AbstractStateStorage<File, TimestampValidi
|
||||
remove(file);
|
||||
}
|
||||
|
||||
private static class FileKeyDescriptor implements KeyDescriptor<File> {
|
||||
private final byte[] buffer = IOUtil.allocReadWriteUTFBuffer();
|
||||
|
||||
public void save(DataOutput out, File value) throws IOException {
|
||||
IOUtil.writeUTFFast(buffer, out, value.getPath());
|
||||
}
|
||||
|
||||
public File read(DataInput in) throws IOException {
|
||||
return new File(IOUtil.readUTFFast(buffer, in));
|
||||
}
|
||||
|
||||
public int getHashCode(File value) {
|
||||
return FileUtil.fileHashCode(value);
|
||||
}
|
||||
|
||||
public boolean isEqual(File val1, File val2) {
|
||||
return FileUtil.filesEqual(val1, val2);
|
||||
}
|
||||
}
|
||||
|
||||
private static class StateExternalizer implements DataExternalizer<TimestampValidityState> {
|
||||
|
||||
public void save(DataOutput out, TimestampValidityState value) throws IOException {
|
||||
|
||||
@@ -16,7 +16,7 @@ public class ClassRenameTest extends IncrementalTestCase {
|
||||
doTest().assertSuccessful();
|
||||
}
|
||||
|
||||
public void _testChangeCaseOfName() {
|
||||
public void testChangeCaseOfName() {
|
||||
doTest().assertSuccessful();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,7 +374,8 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
|
||||
if (dstOffset == srcEnd) return;
|
||||
assert !srcRange.containsOffset(dstOffset);
|
||||
|
||||
CharSequence replacement = getCharsSequence().subSequence(srcStart, srcEnd);
|
||||
//CharSequence replacement = getCharsSequence().subSequence(srcStart, srcEnd);
|
||||
String replacement = getCharsSequence().subSequence(srcStart, srcEnd).toString();
|
||||
|
||||
insertString(dstOffset, replacement);
|
||||
int shift = 0;
|
||||
|
||||
+248
-107
@@ -45,7 +45,7 @@ import java.util.*;
|
||||
* <p/>
|
||||
* I.e. the general idea is to have a language-specific rules hidden by generic arrangement API and common arrangement
|
||||
* engine which works on top of that API and performs the arrangement.
|
||||
*
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since 7/20/12 1:56 PM
|
||||
*/
|
||||
@@ -53,7 +53,7 @@ public class ArrangementEngine {
|
||||
|
||||
/**
|
||||
* Arranges given PSI root contents that belong to the given ranges.
|
||||
*
|
||||
*
|
||||
* @param file target PSI root
|
||||
* @param ranges target ranges to use within the given root
|
||||
*/
|
||||
@@ -80,7 +80,7 @@ public class ArrangementEngine {
|
||||
if (arrangementRules.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
final DocumentEx documentEx;
|
||||
if (document instanceof DocumentEx && !((DocumentEx)document).isInBulkUpdate()) {
|
||||
documentEx = (DocumentEx)document;
|
||||
@@ -196,7 +196,7 @@ public class ArrangementEngine {
|
||||
|
||||
/**
|
||||
* Arranges (re-orders) given entries according to the given rules.
|
||||
*
|
||||
*
|
||||
* @param entries entries to arrange
|
||||
* @param rules rules to use for arrangement
|
||||
* @param <E> arrangement entry type
|
||||
@@ -226,7 +226,7 @@ public class ArrangementEngine {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Set<E> matched = new HashSet<E>();
|
||||
|
||||
for (ArrangementRule rule : rules) {
|
||||
@@ -256,7 +256,7 @@ public class ArrangementEngine {
|
||||
|
||||
return arranged;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <E extends ArrangementEntry> void doArrange(@NotNull List<ArrangementEntryWrapper<E>> wrappers,
|
||||
@NotNull Context<E> context) {
|
||||
@@ -270,12 +270,12 @@ public class ArrangementEngine {
|
||||
List<E> arranged = arrange(map.keySet(), context.rules);
|
||||
|
||||
|
||||
context.prepare(wrappers.get(0).getParent());
|
||||
context.changer.prepare(wrappers, context);
|
||||
// We apply changes from the last position to the first position in order not to bother with offsets shifts.
|
||||
for (int i = arranged.size() - 1; i >= 0; i--) {
|
||||
ArrangementEntryWrapper<E> arrangedWrapper = map.get(arranged.get(i));
|
||||
ArrangementEntryWrapper<E> initialWrapper = wrappers.get(i);
|
||||
context.replace(arrangedWrapper, initialWrapper, i > 0 ? map.get(arranged.get(i - 1)) : null);
|
||||
context.changer.replace(arrangedWrapper, initialWrapper, i > 0 ? map.get(arranged.get(i - 1)) : null, context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,22 +285,21 @@ public class ArrangementEngine {
|
||||
@NotNull public final Collection<ArrangementEntryWrapper<E>> wrappers;
|
||||
@NotNull public final Document document;
|
||||
@NotNull public final List<? extends ArrangementRule> rules;
|
||||
@NotNull public final CodeStyleSettings mySettings;
|
||||
|
||||
@NotNull private String myParentText;
|
||||
private int myParentShift;
|
||||
@NotNull public final CodeStyleSettings settings;
|
||||
@NotNull public final Changer changer;
|
||||
|
||||
private Context(@NotNull Rearranger<E> rearranger,
|
||||
@NotNull Collection<ArrangementEntryWrapper<E>> wrappers,
|
||||
@NotNull Document document,
|
||||
@NotNull List<? extends ArrangementRule> rules,
|
||||
@NotNull CodeStyleSettings settings)
|
||||
@NotNull CodeStyleSettings settings, @NotNull Changer changer)
|
||||
{
|
||||
this.rearranger = rearranger;
|
||||
this.wrappers = wrappers;
|
||||
this.document = document;
|
||||
this.rules = rules;
|
||||
mySettings = settings;
|
||||
this.settings = settings;
|
||||
this.changer = changer;
|
||||
}
|
||||
|
||||
public static <T extends ArrangementEntry> Context<T> from(@NotNull Rearranger<T> rearranger,
|
||||
@@ -322,99 +321,17 @@ public class ArrangementEngine {
|
||||
wrappers.add(wrapper);
|
||||
previous = wrapper;
|
||||
}
|
||||
return new Context<T>(rearranger, wrappers, document, rules, settings);
|
||||
}
|
||||
|
||||
public void prepare(@Nullable ArrangementEntryWrapper<E> parent) {
|
||||
if (parent == null) {
|
||||
myParentText = document.getText();
|
||||
myParentShift = 0;
|
||||
}
|
||||
else {
|
||||
myParentText = document.getCharsSequence().subSequence(parent.getStartOffset(), parent.getEndOffset()).toString();
|
||||
myParentShift = parent.getStartOffset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces given 'old entry' by the given 'new entry'.
|
||||
*
|
||||
* @param newWrapper wrapper for an entry which text should replace given 'old entry' range
|
||||
* @param oldWrapper wrapper for an entry which range should be replaced by the given 'new entry'
|
||||
* @param previous wrapper which will be previous for the entry referenced via the given 'new wrapper'
|
||||
*/
|
||||
@SuppressWarnings("AssignmentToForLoopParameter")
|
||||
public void replace(@NotNull ArrangementEntryWrapper<E> newWrapper,
|
||||
@NotNull ArrangementEntryWrapper<E> oldWrapper,
|
||||
@Nullable ArrangementEntryWrapper<E> previous)
|
||||
{
|
||||
// Calculate blank lines before the arrangement.
|
||||
int blankLinesBefore = 0;
|
||||
TIntArrayList lineFeedOffsets = new TIntArrayList();
|
||||
int oldStartLine = document.getLineNumber(oldWrapper.getStartOffset());
|
||||
if (oldStartLine > 0) {
|
||||
int lastLineFeed = document.getLineStartOffset(oldStartLine) - 1;
|
||||
lineFeedOffsets.add(lastLineFeed);
|
||||
for (int i = lastLineFeed - 1 - myParentShift; i >= 0; i--) {
|
||||
i = CharArrayUtil.shiftBackward(myParentText, i, " \t");
|
||||
if (myParentText.charAt(i) == '\n') {
|
||||
blankLinesBefore++;
|
||||
lineFeedOffsets.add(i + myParentShift);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArrangementEntryWrapper<E> parentWrapper = oldWrapper.getParent();
|
||||
int desiredBlankLinesNumber = rearranger.getBlankLines(mySettings,
|
||||
parentWrapper == null ? null : parentWrapper.getEntry(),
|
||||
previous == null ? null : previous.getEntry(),
|
||||
newWrapper.getEntry());
|
||||
if (desiredBlankLinesNumber == blankLinesBefore && newWrapper.equals(oldWrapper)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String newEntryText = myParentText.substring(newWrapper.getStartOffset() - myParentShift, newWrapper.getEndOffset() - myParentShift);
|
||||
int lineFeedsDiff = desiredBlankLinesNumber - blankLinesBefore;
|
||||
if (lineFeedsDiff == 0 || desiredBlankLinesNumber < 0) {
|
||||
document.replaceString(oldWrapper.getStartOffset(), oldWrapper.getEndOffset(), newEntryText);
|
||||
return;
|
||||
}
|
||||
|
||||
if (lineFeedsDiff > 0) {
|
||||
// Insert necessary number of blank lines.
|
||||
StringBuilder buffer = new StringBuilder(StringUtil.repeat("\n", lineFeedsDiff));
|
||||
buffer.append(newEntryText);
|
||||
document.replaceString(oldWrapper.getStartOffset(), oldWrapper.getEndOffset(), buffer);
|
||||
}
|
||||
else {
|
||||
// Cut exceeding blank lines.
|
||||
int replacementStartOffset = lineFeedOffsets.get(-lineFeedsDiff) + 1;
|
||||
document.replaceString(replacementStartOffset, oldWrapper.getEndOffset(), newEntryText);
|
||||
}
|
||||
|
||||
// Update wrapper ranges.
|
||||
ArrangementEntryWrapper<E> parent = oldWrapper.getParent();
|
||||
if (parent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Deque<ArrangementEntryWrapper<E>> parents = new ArrayDeque<ArrangementEntryWrapper<E>>();
|
||||
do {
|
||||
parents.add(parent);
|
||||
parent.setEndOffset(parent.getEndOffset() + lineFeedsDiff);
|
||||
parent = parent.getParent();
|
||||
} while (parent != null);
|
||||
|
||||
|
||||
while (!parents.isEmpty()) {
|
||||
|
||||
for (ArrangementEntryWrapper<E> wrapper = parents.removeLast().getNext(); wrapper != null; wrapper = wrapper.getNext()) {
|
||||
wrapper.applyShift(lineFeedsDiff);
|
||||
}
|
||||
}
|
||||
Changer changer;
|
||||
// TODO den remove
|
||||
changer = new NormalChanger();
|
||||
// TODO den uncomment
|
||||
//if (document instanceof DocumentEx) {
|
||||
// changer = new RangeMarkerAwareChanger<T>((DocumentEx)document);
|
||||
//}
|
||||
//else {
|
||||
// changer = new NormalChanger();
|
||||
//}
|
||||
return new Context<T>(rearranger, wrappers, document, rules, settings, changer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,4 +347,228 @@ public class ArrangementEngine {
|
||||
end = start + count;
|
||||
}
|
||||
}
|
||||
|
||||
private interface Changer<E extends ArrangementEntry> {
|
||||
void prepare(@NotNull List<ArrangementEntryWrapper<E>> toArrange, @NotNull Context<E> context);
|
||||
|
||||
/**
|
||||
* Replaces given 'old entry' by the given 'new entry'.
|
||||
*
|
||||
* @param newWrapper wrapper for an entry which text should replace given 'old entry' range
|
||||
* @param oldWrapper wrapper for an entry which range should be replaced by the given 'new entry'
|
||||
* @param previous wrapper which will be previous for the entry referenced via the given 'new wrapper'
|
||||
* @param context current context
|
||||
*/
|
||||
void replace(@NotNull ArrangementEntryWrapper<E> newWrapper,
|
||||
@NotNull ArrangementEntryWrapper<E> oldWrapper,
|
||||
@Nullable ArrangementEntryWrapper<E> previous,
|
||||
@NotNull Context<E> context);
|
||||
}
|
||||
|
||||
private static class NormalChanger<E extends ArrangementEntry> implements Changer<E> {
|
||||
|
||||
@NotNull private String myParentText;
|
||||
private int myParentShift;
|
||||
|
||||
@Override
|
||||
public void prepare(@NotNull List<ArrangementEntryWrapper<E>> toArrange, @NotNull Context<E> context) {
|
||||
ArrangementEntryWrapper<E> parent = toArrange.get(0).getParent();
|
||||
if (parent == null) {
|
||||
myParentText = context.document.getText();
|
||||
myParentShift = 0;
|
||||
}
|
||||
else {
|
||||
myParentText = context.document.getCharsSequence().subSequence(parent.getStartOffset(), parent.getEndOffset()).toString();
|
||||
myParentShift = parent.getStartOffset();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("AssignmentToForLoopParameter")
|
||||
@Override
|
||||
public void replace(@NotNull ArrangementEntryWrapper<E> newWrapper,
|
||||
@NotNull ArrangementEntryWrapper<E> oldWrapper,
|
||||
@Nullable ArrangementEntryWrapper<E> previous,
|
||||
@NotNull Context<E> context)
|
||||
{
|
||||
// Calculate blank lines before the arrangement.
|
||||
int blankLinesBefore = 0;
|
||||
TIntArrayList lineFeedOffsets = new TIntArrayList();
|
||||
int oldStartLine = context.document.getLineNumber(oldWrapper.getStartOffset());
|
||||
if (oldStartLine > 0) {
|
||||
int lastLineFeed = context.document.getLineStartOffset(oldStartLine) - 1;
|
||||
lineFeedOffsets.add(lastLineFeed);
|
||||
for (int i = lastLineFeed - 1 - myParentShift; i >= 0; i--) {
|
||||
i = CharArrayUtil.shiftBackward(myParentText, i, " \t");
|
||||
if (myParentText.charAt(i) == '\n') {
|
||||
blankLinesBefore++;
|
||||
lineFeedOffsets.add(i + myParentShift);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArrangementEntryWrapper<E> parentWrapper = oldWrapper.getParent();
|
||||
int desiredBlankLinesNumber = context.rearranger.getBlankLines(context.settings,
|
||||
parentWrapper == null ? null : parentWrapper.getEntry(),
|
||||
previous == null ? null : previous.getEntry(),
|
||||
newWrapper.getEntry());
|
||||
if (desiredBlankLinesNumber == blankLinesBefore && newWrapper.equals(oldWrapper)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String newEntryText = myParentText.substring(newWrapper.getStartOffset() - myParentShift, newWrapper.getEndOffset() - myParentShift);
|
||||
int lineFeedsDiff = desiredBlankLinesNumber - blankLinesBefore;
|
||||
if (lineFeedsDiff == 0 || desiredBlankLinesNumber < 0) {
|
||||
context.document.replaceString(oldWrapper.getStartOffset(), oldWrapper.getEndOffset(), newEntryText);
|
||||
return;
|
||||
}
|
||||
|
||||
if (lineFeedsDiff > 0) {
|
||||
// Insert necessary number of blank lines.
|
||||
StringBuilder buffer = new StringBuilder(StringUtil.repeat("\n", lineFeedsDiff));
|
||||
buffer.append(newEntryText);
|
||||
context.document.replaceString(oldWrapper.getStartOffset(), oldWrapper.getEndOffset(), buffer);
|
||||
}
|
||||
else {
|
||||
// Cut exceeding blank lines.
|
||||
int replacementStartOffset = lineFeedOffsets.get(-lineFeedsDiff) + 1;
|
||||
context.document.replaceString(replacementStartOffset, oldWrapper.getEndOffset(), newEntryText);
|
||||
}
|
||||
|
||||
// Update wrapper ranges.
|
||||
ArrangementEntryWrapper<E> parent = oldWrapper.getParent();
|
||||
if (parent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Deque<ArrangementEntryWrapper<E>> parents = new ArrayDeque<ArrangementEntryWrapper<E>>();
|
||||
do {
|
||||
parents.add(parent);
|
||||
parent.setEndOffset(parent.getEndOffset() + lineFeedsDiff);
|
||||
parent = parent.getParent();
|
||||
}
|
||||
while (parent != null);
|
||||
|
||||
|
||||
while (!parents.isEmpty()) {
|
||||
|
||||
for (ArrangementEntryWrapper<E> wrapper = parents.removeLast().getNext(); wrapper != null; wrapper = wrapper.getNext()) {
|
||||
wrapper.applyShift(lineFeedsDiff);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class RangeMarkerAwareChanger<E extends ArrangementEntry> implements Changer<E> {
|
||||
|
||||
@NotNull private final Set<ArrangementEntryWrapper<E>> myNotMoved = new HashSet<ArrangementEntryWrapper<E>>();
|
||||
@NotNull private final DocumentEx myDocument;
|
||||
|
||||
RangeMarkerAwareChanger(@NotNull DocumentEx document) {
|
||||
myDocument = document;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepare(@NotNull List<ArrangementEntryWrapper<E>> toArrange, @NotNull Context<E> context) {
|
||||
myNotMoved.clear();
|
||||
myNotMoved.addAll(toArrange);
|
||||
for (ArrangementEntryWrapper<E> wrapper : toArrange) {
|
||||
wrapper.updateBlankLines(myDocument);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("AssignmentToForLoopParameter")
|
||||
@Override
|
||||
public void replace(@NotNull ArrangementEntryWrapper<E> newWrapper,
|
||||
@NotNull ArrangementEntryWrapper<E> oldWrapper,
|
||||
@Nullable ArrangementEntryWrapper<E> previous,
|
||||
@NotNull Context<E> context)
|
||||
{
|
||||
// Calculate blank lines before the arrangement.
|
||||
int blankLinesBefore = oldWrapper.getBlankLinesBefore();
|
||||
|
||||
ArrangementEntryWrapper<E> parentWrapper = oldWrapper.getParent();
|
||||
int desiredBlankLinesNumber = context.rearranger.getBlankLines(context.settings,
|
||||
parentWrapper == null ? null : parentWrapper.getEntry(),
|
||||
previous == null ? null : previous.getEntry(),
|
||||
newWrapper.getEntry());
|
||||
if ((desiredBlankLinesNumber < 0 || desiredBlankLinesNumber == blankLinesBefore) && newWrapper.equals(oldWrapper)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int lineFeedsDiff = desiredBlankLinesNumber - blankLinesBefore;
|
||||
int insertionOffset = oldWrapper.getStartOffset();
|
||||
if (oldWrapper.getStartOffset() > newWrapper.getStartOffset()) {
|
||||
insertionOffset -= newWrapper.getEndOffset() - newWrapper.getStartOffset();
|
||||
}
|
||||
myDocument.moveText(newWrapper.getStartOffset(), newWrapper.getEndOffset(), oldWrapper.getStartOffset());
|
||||
newWrapper.placeBefore(oldWrapper);
|
||||
myNotMoved.remove(newWrapper);
|
||||
for (ArrangementEntryWrapper<E> w : myNotMoved) {
|
||||
if (w.getStartOffset() >= oldWrapper.getStartOffset() && w.getStartOffset() < newWrapper.getStartOffset()) {
|
||||
w.applyShift(newWrapper.getEndOffset() - newWrapper.getStartOffset());
|
||||
}
|
||||
else if (w.getStartOffset() < oldWrapper.getStartOffset() && w.getStartOffset() > newWrapper.getStartOffset()) {
|
||||
w.applyShift(newWrapper.getStartOffset() - newWrapper.getEndOffset());
|
||||
}
|
||||
}
|
||||
|
||||
if (desiredBlankLinesNumber >= 0 && lineFeedsDiff > 0) {
|
||||
myDocument.insertString(insertionOffset, StringUtil.repeat("\n", lineFeedsDiff));
|
||||
shiftOffsets(newWrapper, null, lineFeedsDiff);
|
||||
}
|
||||
|
||||
if (desiredBlankLinesNumber >= 0 && lineFeedsDiff < 0) {
|
||||
// Cut exceeding blank lines.
|
||||
int replacementStartOffset = getBlankLineOffset(-lineFeedsDiff, insertionOffset);
|
||||
myDocument.deleteString(replacementStartOffset, insertionOffset);
|
||||
shiftOffsets(oldWrapper, null, lineFeedsDiff);
|
||||
}
|
||||
|
||||
// Update wrapper ranges.
|
||||
if (desiredBlankLinesNumber < 0 || lineFeedsDiff == 0 || parentWrapper == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Deque<ArrangementEntryWrapper<E>> parents = new ArrayDeque<ArrangementEntryWrapper<E>>();
|
||||
do {
|
||||
parents.add(parentWrapper);
|
||||
parentWrapper.setEndOffset(parentWrapper.getEndOffset() + lineFeedsDiff);
|
||||
parentWrapper = parentWrapper.getParent();
|
||||
}
|
||||
while (parentWrapper != null);
|
||||
|
||||
|
||||
while (!parents.isEmpty()) {
|
||||
for (ArrangementEntryWrapper<E> wrapper = parents.removeLast().getNext(); wrapper != null; wrapper = wrapper.getNext()) {
|
||||
wrapper.applyShift(lineFeedsDiff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getBlankLineOffset(int blankLinesNumber, int startOffset) {
|
||||
int startLine = myDocument.getLineNumber(startOffset);
|
||||
if (startLine <= 0) {
|
||||
return 0;
|
||||
}
|
||||
CharSequence text = myDocument.getCharsSequence();
|
||||
for (int i = myDocument.getLineStartOffset(startLine - 1) - 1; i >= 0; i = CharArrayUtil.lastIndexOf(text, "\n", i - 1)) {
|
||||
if (--blankLinesNumber <= 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void shiftOffsets(@Nullable ArrangementEntryWrapper<?> first, @Nullable ArrangementEntryWrapper<?> last, int shift) {
|
||||
if (first == null) {
|
||||
return;
|
||||
}
|
||||
for (ArrangementEntryWrapper<?> wrapper = first; wrapper != null && wrapper != last; wrapper = wrapper.getNext()) {
|
||||
wrapper.applyShift(shift);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+52
-3
@@ -15,8 +15,10 @@
|
||||
*/
|
||||
package com.intellij.psi.codeStyle.arrangement.engine;
|
||||
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.codeStyle.arrangement.ArrangementEntry;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -36,7 +38,7 @@ import java.util.List;
|
||||
* </pre>
|
||||
* <p/>
|
||||
* Not thread-safe.
|
||||
*
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since 8/31/12 12:06 PM
|
||||
*/
|
||||
@@ -51,15 +53,22 @@ public class ArrangementEntryWrapper<E extends ArrangementEntry> {
|
||||
|
||||
private int myStartOffset;
|
||||
private int myEndOffset;
|
||||
private int myBlankLinesBefore;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ArrangementEntryWrapper(@NotNull E entry) {
|
||||
myEntry = entry;
|
||||
myStartOffset = entry.getStartOffset();
|
||||
myEndOffset = entry.getEndOffset();
|
||||
ArrangementEntryWrapper<E> previous = null;
|
||||
for (ArrangementEntry child : entry.getChildren()) {
|
||||
ArrangementEntryWrapper<E> childWrapper = new ArrangementEntryWrapper<E>((E)child);
|
||||
childWrapper.setParent(this);
|
||||
if (previous != null) {
|
||||
previous.setNext(childWrapper);
|
||||
childWrapper.setPrevious(previous);
|
||||
}
|
||||
previous = childWrapper;
|
||||
myChildren.add(childWrapper);
|
||||
}
|
||||
}
|
||||
@@ -104,6 +113,46 @@ public class ArrangementEntryWrapper<E extends ArrangementEntry> {
|
||||
return myNext;
|
||||
}
|
||||
|
||||
public void placeBefore(@NotNull ArrangementEntryWrapper<E> anchor) {
|
||||
if (myNext != null) {
|
||||
myNext.setPrevious(myPrevious);
|
||||
}
|
||||
if (myPrevious != null) {
|
||||
myPrevious.setNext(myNext);
|
||||
}
|
||||
setPrevious(anchor.getPrevious());
|
||||
if (myPrevious != null) {
|
||||
myPrevious.setNext(this);
|
||||
}
|
||||
setNext(anchor);
|
||||
anchor.setPrevious(this);
|
||||
}
|
||||
|
||||
public int getBlankLinesBefore() {
|
||||
return myBlankLinesBefore;
|
||||
}
|
||||
|
||||
@SuppressWarnings("AssignmentToForLoopParameter")
|
||||
public void updateBlankLines(@NotNull Document document) {
|
||||
int startLine = document.getLineNumber(getStartOffset());
|
||||
myBlankLinesBefore = 0;
|
||||
if (startLine <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
CharSequence text = document.getCharsSequence();
|
||||
int lastLineFeed = document.getLineStartOffset(startLine) - 1;
|
||||
for (int i = lastLineFeed - 1; i >= 0; i--) {
|
||||
i = CharArrayUtil.shiftBackward(text, i, " \t");
|
||||
if (text.charAt(i) == '\n') {
|
||||
++myBlankLinesBefore;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setNext(@Nullable ArrangementEntryWrapper<E> next) {
|
||||
myNext = next;
|
||||
}
|
||||
@@ -120,7 +169,7 @@ public class ArrangementEntryWrapper<E extends ArrangementEntry> {
|
||||
child.applyShift(shift);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return myEntry.hashCode();
|
||||
@@ -141,6 +190,6 @@ public class ArrangementEntryWrapper<E extends ArrangementEntry> {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return myEntry.toString();
|
||||
return String.format("range: [%d; %d), entry: %s", myStartOffset, myEndOffset, myEntry.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,9 +96,10 @@ public class RefreshSessionImpl extends RefreshSession {
|
||||
if (!workQueue.isEmpty()) {
|
||||
((LocalFileSystemImpl)LocalFileSystem.getInstance()).markSuspiciousFilesDirty(workQueue);
|
||||
|
||||
final FileWatcher watcher = FileWatcher.getInstance();
|
||||
for (VirtualFile file : workQueue) {
|
||||
final NewVirtualFile nvf = (NewVirtualFile)file;
|
||||
if (!myIsRecursive && (!myIsAsync || !FileWatcher.getInstance().isWatched(nvf))) { // We're unable to definitely refresh synchronously by means of file watcher.
|
||||
if (!myIsRecursive && (!myIsAsync || !watcher.isWatched(nvf))) { // We're unable to definitely refresh synchronously by means of file watcher.
|
||||
nvf.markDirty();
|
||||
}
|
||||
|
||||
|
||||
+12
-17
@@ -183,7 +183,7 @@ public class PersistentFSImpl extends PersistentFS implements ApplicationCompone
|
||||
final FakeVirtualFile child = new FakeVirtualFile(file, name);
|
||||
final FileAttributes attributes = fs.getAttributes(child);
|
||||
if (attributes != null) {
|
||||
final int childId = createAndCopyRecord(fs, child, id, attributes);
|
||||
final int childId = createAndFillRecord(fs, child, id, attributes);
|
||||
childrenIds[i] = childId;
|
||||
}
|
||||
else {
|
||||
@@ -285,11 +285,11 @@ public class PersistentFSImpl extends PersistentFS implements ApplicationCompone
|
||||
return FSRecords.getModCount();
|
||||
}
|
||||
|
||||
private static boolean copyRecordFromDelegateFS(final int id,
|
||||
final int parentId,
|
||||
@NotNull VirtualFile file,
|
||||
@NotNull NewVirtualFileSystem fs,
|
||||
@NotNull FileAttributes attributes) {
|
||||
private static boolean writeAttributesToRecord(final int id,
|
||||
final int parentId,
|
||||
@NotNull VirtualFile file,
|
||||
@NotNull NewVirtualFileSystem fs,
|
||||
@NotNull FileAttributes attributes) {
|
||||
String name = file.getName();
|
||||
if (!name.isEmpty()) {
|
||||
if (namesEqual(fs, name, FSRecords.getName(id))) return false; // TODO: Handle root attributes change.
|
||||
@@ -396,7 +396,7 @@ public class PersistentFSImpl extends PersistentFS implements ApplicationCompone
|
||||
final VirtualFile fake = new FakeVirtualFile(parent, childName);
|
||||
final FileAttributes attributes = fs.getAttributes(fake);
|
||||
if (attributes != null) {
|
||||
final int child = createAndCopyRecord(fs, fake, parentId, attributes);
|
||||
final int child = createAndFillRecord(fs, fake, parentId, attributes);
|
||||
FSRecords.updateList(parentId, ArrayUtil.append(children, child));
|
||||
return child;
|
||||
}
|
||||
@@ -778,7 +778,7 @@ public class PersistentFSImpl extends PersistentFS implements ApplicationCompone
|
||||
return null;
|
||||
}
|
||||
|
||||
final boolean newRoot = copyRecordFromDelegateFS(rootId, 0, root, fs, attributes);
|
||||
final boolean newRoot = writeAttributesToRecord(rootId, 0, root, fs, attributes);
|
||||
if (!newRoot) {
|
||||
if (attributes.lastModified != FSRecords.getTimestamp(rootId)) {
|
||||
root.markDirtyRecursively();
|
||||
@@ -937,7 +937,7 @@ public class PersistentFSImpl extends PersistentFS implements ApplicationCompone
|
||||
}
|
||||
else if (event instanceof VFileCopyEvent) {
|
||||
final VFileCopyEvent copyEvent = (VFileCopyEvent)event;
|
||||
executeCopy(copyEvent.getFile(), copyEvent.getNewParent(), copyEvent.getNewChildName());
|
||||
executeCreateChild(copyEvent.getNewParent(), copyEvent.getNewChildName());
|
||||
}
|
||||
else if (event instanceof VFileMoveEvent) {
|
||||
final VFileMoveEvent moveEvent = (VFileMoveEvent)event;
|
||||
@@ -971,7 +971,7 @@ public class PersistentFSImpl extends PersistentFS implements ApplicationCompone
|
||||
final FileAttributes attributes = delegate.getAttributes(fake);
|
||||
if (attributes != null) {
|
||||
final int parentId = getFileId(parent);
|
||||
final int childId = createAndCopyRecord(delegate, fake, parentId, attributes);
|
||||
final int childId = createAndFillRecord(delegate, fake, parentId, attributes);
|
||||
appendIdToParentList(parentId, childId);
|
||||
assert parent instanceof VirtualDirectoryImpl : parent;
|
||||
final VirtualDirectoryImpl dir = (VirtualDirectoryImpl)parent;
|
||||
@@ -979,12 +979,12 @@ public class PersistentFSImpl extends PersistentFS implements ApplicationCompone
|
||||
}
|
||||
}
|
||||
|
||||
private static int createAndCopyRecord(@NotNull NewVirtualFileSystem delegateSystem,
|
||||
private static int createAndFillRecord(@NotNull NewVirtualFileSystem delegateSystem,
|
||||
@NotNull VirtualFile delegateFile,
|
||||
int parentId,
|
||||
@NotNull FileAttributes attributes) {
|
||||
final int childId = FSRecords.createRecord();
|
||||
copyRecordFromDelegateFS(childId, parentId, delegateFile, delegateSystem, attributes);
|
||||
writeAttributesToRecord(childId, parentId, delegateFile, delegateSystem, attributes);
|
||||
return childId;
|
||||
}
|
||||
|
||||
@@ -1097,11 +1097,6 @@ public class PersistentFSImpl extends PersistentFS implements ApplicationCompone
|
||||
((VirtualFileSystemEntry)file).setModificationStamp(newModificationStamp);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"UnusedDeclaration"})
|
||||
private static void executeCopy(VirtualFile from, @NotNull VirtualFile newParent, @NotNull String copyName) {
|
||||
executeCreateChild(newParent, copyName);
|
||||
}
|
||||
|
||||
private void executeMove(@NotNull VirtualFile file, @NotNull VirtualFile newParent) {
|
||||
clearIdCache();
|
||||
|
||||
|
||||
+23
-39
@@ -130,7 +130,7 @@ public class FileWatcherTest extends PlatformLangTestCase {
|
||||
final LocalFileSystem.WatchRequest request = watch(file);
|
||||
try {
|
||||
myAccept = true;
|
||||
writeToFile(file, "new content");
|
||||
FileUtil.writeToFile(file, "new content");
|
||||
assertEvent(VFileContentChangeEvent.class, file.getAbsolutePath());
|
||||
|
||||
myAccept = true;
|
||||
@@ -138,7 +138,7 @@ public class FileWatcherTest extends PlatformLangTestCase {
|
||||
assertEvent(VFileDeleteEvent.class, file.getAbsolutePath());
|
||||
|
||||
myAccept = true;
|
||||
writeToFile(file, "re-creation");
|
||||
FileUtil.writeToFile(file, "re-creation");
|
||||
assertEvent(VFileCreateEvent.class, file.getAbsolutePath());
|
||||
}
|
||||
finally {
|
||||
@@ -147,22 +147,6 @@ public class FileWatcherTest extends PlatformLangTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeToFile(File file, String text) throws IOException {
|
||||
//try {
|
||||
// Thread.sleep(1000);
|
||||
//}
|
||||
//catch (InterruptedException e) {
|
||||
// LOG.error(e);
|
||||
//}
|
||||
FileUtil.writeToFile(file, text);
|
||||
//try {
|
||||
// Thread.sleep(1000);
|
||||
//}
|
||||
//catch (InterruptedException e) {
|
||||
// LOG.error(e);
|
||||
//}
|
||||
}
|
||||
|
||||
public void testNonCanonicallyNamedFileRoot() throws Exception {
|
||||
if (SystemInfo.isFileSystemCaseSensitive) {
|
||||
System.err.println("Ignored: case-insensitive FS required");
|
||||
@@ -176,7 +160,7 @@ public class FileWatcherTest extends PlatformLangTestCase {
|
||||
final LocalFileSystem.WatchRequest request = watch(new File(watchRoot));
|
||||
try {
|
||||
myAccept = true;
|
||||
writeToFile(file, "new content");
|
||||
FileUtil.writeToFile(file, "new content");
|
||||
assertEvent(VFileContentChangeEvent.class, file.getAbsolutePath());
|
||||
|
||||
myAccept = true;
|
||||
@@ -184,7 +168,7 @@ public class FileWatcherTest extends PlatformLangTestCase {
|
||||
assertEvent(VFileDeleteEvent.class, file.getAbsolutePath());
|
||||
|
||||
myAccept = true;
|
||||
writeToFile(file, "re-creation");
|
||||
FileUtil.writeToFile(file, "re-creation");
|
||||
assertEvent(VFileCreateEvent.class, file.getAbsolutePath());
|
||||
}
|
||||
finally {
|
||||
@@ -209,7 +193,7 @@ public class FileWatcherTest extends PlatformLangTestCase {
|
||||
assertEvent(VFileCreateEvent.class, file.getAbsolutePath());
|
||||
|
||||
myAccept = true;
|
||||
writeToFile(file, "new content");
|
||||
FileUtil.writeToFile(file, "new content");
|
||||
assertEvent(VFileContentChangeEvent.class, file.getAbsolutePath());
|
||||
|
||||
myAccept = true;
|
||||
@@ -217,7 +201,7 @@ public class FileWatcherTest extends PlatformLangTestCase {
|
||||
assertEvent(VFileDeleteEvent.class, file.getAbsolutePath());
|
||||
|
||||
myAccept = true;
|
||||
writeToFile(file, "re-creation");
|
||||
FileUtil.writeToFile(file, "re-creation");
|
||||
assertEvent(VFileCreateEvent.class, file.getAbsolutePath());
|
||||
}
|
||||
finally {
|
||||
@@ -236,13 +220,13 @@ public class FileWatcherTest extends PlatformLangTestCase {
|
||||
final LocalFileSystem.WatchRequest request = watch(topDir, false);
|
||||
try {
|
||||
myAccept = true;
|
||||
writeToFile(watchedFile, "new content");
|
||||
FileUtil.writeToFile(watchedFile, "new content");
|
||||
assertEvent(VFileContentChangeEvent.class, watchedFile.getAbsolutePath());
|
||||
|
||||
myAccept = true;
|
||||
try {
|
||||
myTimeout = 10 * INTER_RESPONSE_DELAY;
|
||||
writeToFile(unwatchedFile, "new content");
|
||||
FileUtil.writeToFile(unwatchedFile, "new content");
|
||||
assertEvent(VFileEvent.class);
|
||||
}
|
||||
finally {
|
||||
@@ -269,9 +253,9 @@ public class FileWatcherTest extends PlatformLangTestCase {
|
||||
final LocalFileSystem.WatchRequest subRequest = watch(sub2Dir);
|
||||
try {
|
||||
myAccept = true;
|
||||
writeToFile(watchedFile1, "new content");
|
||||
writeToFile(watchedFile2, "new content");
|
||||
writeToFile(unwatchedFile, "new content");
|
||||
FileUtil.writeToFile(watchedFile1, "new content");
|
||||
FileUtil.writeToFile(watchedFile2, "new content");
|
||||
FileUtil.writeToFile(unwatchedFile, "new content");
|
||||
assertEvent(VFileContentChangeEvent.class, watchedFile1.getAbsolutePath(), watchedFile2.getAbsolutePath());
|
||||
}
|
||||
finally {
|
||||
@@ -294,7 +278,7 @@ public class FileWatcherTest extends PlatformLangTestCase {
|
||||
refresh(subDir);
|
||||
|
||||
myAccept = true;
|
||||
writeToFile(file, "new content");
|
||||
FileUtil.writeToFile(file, "new content");
|
||||
assertEvent(VFileCreateEvent.class, file.getAbsolutePath());
|
||||
}
|
||||
finally {
|
||||
@@ -317,17 +301,17 @@ public class FileWatcherTest extends PlatformLangTestCase {
|
||||
final LocalFileSystem.WatchRequest requestForSideDir = watch(sideDir);
|
||||
try {
|
||||
myAccept = true;
|
||||
writeToFile(fileInTopDir, "new content");
|
||||
writeToFile(fileInSubDir, "new content");
|
||||
writeToFile(fileInSideDir, "new content");
|
||||
FileUtil.writeToFile(fileInTopDir, "new content");
|
||||
FileUtil.writeToFile(fileInSubDir, "new content");
|
||||
FileUtil.writeToFile(fileInSideDir, "new content");
|
||||
assertEvent(VFileContentChangeEvent.class, fileInSubDir.getAbsolutePath(), fileInSideDir.getAbsolutePath());
|
||||
|
||||
final LocalFileSystem.WatchRequest requestForTopDir = watch(topDir);
|
||||
try {
|
||||
myAccept = true;
|
||||
writeToFile(fileInTopDir, "newer content");
|
||||
writeToFile(fileInSubDir, "newer content");
|
||||
writeToFile(fileInSideDir, "newer content");
|
||||
FileUtil.writeToFile(fileInTopDir, "newer content");
|
||||
FileUtil.writeToFile(fileInSubDir, "newer content");
|
||||
FileUtil.writeToFile(fileInSideDir, "newer content");
|
||||
assertEvent(VFileContentChangeEvent.class, fileInTopDir.getAbsolutePath(), fileInSubDir.getAbsolutePath(), fileInSideDir.getAbsolutePath());
|
||||
}
|
||||
finally {
|
||||
@@ -335,9 +319,9 @@ public class FileWatcherTest extends PlatformLangTestCase {
|
||||
}
|
||||
|
||||
myAccept = true;
|
||||
writeToFile(fileInTopDir, "newest content");
|
||||
writeToFile(fileInSubDir, "newest content");
|
||||
writeToFile(fileInSideDir, "newest content");
|
||||
FileUtil.writeToFile(fileInTopDir, "newest content");
|
||||
FileUtil.writeToFile(fileInSubDir, "newest content");
|
||||
FileUtil.writeToFile(fileInSideDir, "newest content");
|
||||
assertEvent(VFileContentChangeEvent.class, fileInSubDir.getAbsolutePath(), fileInSideDir.getAbsolutePath());
|
||||
|
||||
myAccept = true;
|
||||
@@ -438,7 +422,7 @@ public class FileWatcherTest extends PlatformLangTestCase {
|
||||
final LocalFileSystem.WatchRequest request = watch(substDir);
|
||||
try {
|
||||
myAccept = true;
|
||||
writeToFile(file, "new content");
|
||||
FileUtil.writeToFile(file, "new content");
|
||||
assertEvent(VFileContentChangeEvent.class, substFile.getAbsolutePath());
|
||||
|
||||
final LocalFileSystem.WatchRequest request2 = watch(targetDir);
|
||||
@@ -452,7 +436,7 @@ public class FileWatcherTest extends PlatformLangTestCase {
|
||||
}
|
||||
|
||||
myAccept = true;
|
||||
writeToFile(file, "re-creation");
|
||||
FileUtil.writeToFile(file, "re-creation");
|
||||
assertEvent(VFileCreateEvent.class, substFile.getAbsolutePath());
|
||||
}
|
||||
finally {
|
||||
|
||||
Reference in New Issue
Block a user