/*
* SonarQube Java
* Copyright (C) 2012-2016 SonarSource SA
* mailto:contact AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.java.se;
import com.google.common.collect.ImmutableList;
import org.sonar.java.resolve.JavaSymbol;
import org.sonar.java.se.symbolicvalues.SymbolicValue;
import org.sonar.plugins.java.api.semantic.Symbol;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
public class MethodBehavior {
private final Symbol.MethodSymbol methodSymbol;
private final Set<MethodYield> yields;
private final Map<Symbol, SymbolicValue> parameters;
private boolean complete = false;
public MethodBehavior(Symbol.MethodSymbol methodSymbol) {
this.methodSymbol = methodSymbol;
this.yields = new LinkedHashSet<>();
this.parameters = new LinkedHashMap<>();
}
public void createYield(ProgramState programState, boolean happyPathYield) {
MethodYield yield = new MethodYield(parameters.size(), ((JavaSymbol.MethodJavaSymbol) methodSymbol).isVarArgs());
yield.exception = !happyPathYield;
List<SymbolicValue> parameterSymbolicValues = new ArrayList<>(parameters.values());
for (int i = 0; i < yield.parametersConstraints.length; i++) {
yield.parametersConstraints[i] = programState.getConstraint(parameterSymbolicValues.get(i));
}
SymbolicValue resultSV = programState.exitValue();
if (resultSV instanceof SymbolicValue.ExceptionalSymbolicValue) {
yield.exception = true;
yield.exceptionType = ((SymbolicValue.ExceptionalSymbolicValue) resultSV).exceptionType();
} else if (!isConstructor() && !isVoidMethod()) {
if (resultSV == null) {
// if there is no return value but we are not in a void method or constructor, we are not in a happy path
yield.exception = true;
} else {
yield.resultIndex = parameterSymbolicValues.indexOf(resultSV);
yield.resultConstraint = programState.getConstraint(resultSV);
}
}
yields.add(yield);
}
private boolean isVoidMethod() {
return methodSymbol.returnType().type().isVoid();
}
private boolean isConstructor() {
return ((JavaSymbol.MethodJavaSymbol) methodSymbol).isConstructor();
}
List<MethodYield> yields() {
return ImmutableList.<MethodYield>builder().addAll(yields).build();
}
Stream<MethodYield> exceptionalPathYields() {
return yields.stream().filter(y -> y.exception);
}
Stream<MethodYield> happyPathYields() {
return yields.stream().filter(y -> !y.exception);
}
public void addParameter(Symbol symbol, SymbolicValue sv) {
parameters.put(symbol, sv);
}
public Collection<SymbolicValue> parameters() {
return parameters.values();
}
public boolean isComplete() {
return complete;
}
void completed() {
this.complete = true;
}
}