/* * Copyright 2006 Sascha Weinreuter * * 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.intellij.plugins.intelliLang.util; import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInspection.dataFlow.DfaPsiUtil; import com.intellij.codeInspection.dataFlow.DfaUtil; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.util.containers.ContainerUtil; import org.intellij.plugins.intelliLang.Configuration; import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.ConcurrentMap; /** * Computes the constant value of an expression while considering the substitution annotation for non-compile-time * constant expressions. * <p/> * This is a quite simplified implementation at the moment. */ public class SubstitutedExpressionEvaluationHelper { private final PsiConstantEvaluationHelper myHelper; private final Configuration myConfiguration; public SubstitutedExpressionEvaluationHelper(final Project project) { myHelper = JavaPsiFacade.getInstance(project).getConstantEvaluationHelper(); myConfiguration = Configuration.getInstance(); } public Object computeExpression(final PsiExpression e, final List<PsiExpression> uncomputables) { return computeExpression(e, myConfiguration.getAdvancedConfiguration().getDfaOption(), myConfiguration.getAdvancedConfiguration().isIncludeUncomputablesAsLiterals(), uncomputables); } public Object computeExpression(final PsiExpression e, final Configuration.DfaOption dfaOption, final boolean includeUncomputablesAsLiterals, final List<PsiExpression> uncomputables) { final ConcurrentMap<PsiElement, Object> map = ContainerUtil.newConcurrentMap(); //if (true) return myHelper.computeConstantExpression(e, false); return myHelper.computeExpression(e, false, new PsiConstantEvaluationHelper.AuxEvaluator() { @Nullable public Object computeExpression(final PsiExpression o, final PsiConstantEvaluationHelper.AuxEvaluator auxEvaluator) { PsiType resolvedType = null; if (o instanceof PsiMethodCallExpression) { final PsiMethodCallExpression c = (PsiMethodCallExpression)o; final PsiMethod m = (PsiMethod)c.getMethodExpression().resolve(); final PsiType returnType = m != null? m.getReturnType() : null; if (returnType != null && !PsiType.VOID.equals(returnType)) { // find substitution final Object substituted = calcSubstituted(m); if (substituted != null) return substituted; } resolvedType = returnType; } else if (o instanceof PsiReferenceExpression) { final PsiElement resolved = ((PsiReferenceExpression)o).resolve(); if (resolved instanceof PsiModifierListOwner) { // find substitution final Object substituted = calcSubstituted((PsiModifierListOwner)resolved); if (substituted != null) return substituted; if (resolved instanceof PsiVariable) { final PsiVariable psiVariable = (PsiVariable)resolved; resolvedType = psiVariable.getType(); final Collection<PsiExpression> values; if (dfaOption == Configuration.DfaOption.ASSIGNMENTS) { values = DfaPsiUtil.getVariableAssignmentsInFile(psiVariable, true, o); } else if (dfaOption == Configuration.DfaOption.DFA) { final Collection<PsiExpression> realValues = DfaUtil.getCachedVariableValues(psiVariable, o); values = realValues == null? DfaPsiUtil.getVariableAssignmentsInFile(psiVariable, true, o) : realValues; } else { values = Collections.emptyList(); } // return the first computed value as far as we do not support multiple injection for (PsiExpression value : values) { final Object computedValue = auxEvaluator.computeExpression(value, this); if (computedValue != null) { return computedValue; } } } } } if (uncomputables != null) uncomputables.add(o); if (includeUncomputablesAsLiterals) { if (resolvedType != null) { if (PsiPrimitiveType.DOUBLE.isAssignableFrom(resolvedType)) return 1; // magic number! } final StringBuilder sb = new StringBuilder(); o.accept(new PsiRecursiveElementWalkingVisitor() { @Override public void visitElement(PsiElement element) { if (element instanceof PsiExpressionList) return; if (element instanceof PsiIdentifier) { if (sb.length() > 0) sb.append("."); sb.append(element.getText()); } super.visitElement(element); } }); return sb.toString(); } return null; } public ConcurrentMap<PsiElement, Object> getCacheMap(final boolean overflow) { return map; //return PsiManager.getInstance(project).getCachedValuesManager().getCachedValue(project, COMPUTED_MAP_KEY, PROVIDER, false); } }); } @Nullable private Object calcSubstituted(final PsiModifierListOwner owner) { final PsiAnnotation annotation = AnnotationUtil.findAnnotation(owner, myConfiguration.getAdvancedConfiguration().getSubstAnnotationPair().second); if (annotation != null) { return AnnotationUtilEx.calcAnnotationValue(annotation, "value"); } return null; } }