SSR: script log first pass
[idea/community.git] / platform / structuralsearch / source / com / intellij / structuralsearch / plugin / replace / impl / ReplacementBuilder.java
1 package com.intellij.structuralsearch.plugin.replace.impl;
2
3 import com.intellij.codeInsight.template.Template;
4 import com.intellij.codeInsight.template.TemplateManager;
5 import com.intellij.openapi.fileTypes.FileType;
6 import com.intellij.openapi.project.Project;
7 import com.intellij.openapi.util.text.StringUtil;
8 import com.intellij.psi.PsiElement;
9 import com.intellij.psi.PsiRecursiveElementWalkingVisitor;
10 import com.intellij.structuralsearch.MalformedPatternException;
11 import com.intellij.structuralsearch.MatchResult;
12 import com.intellij.structuralsearch.StructuralSearchProfile;
13 import com.intellij.structuralsearch.StructuralSearchUtil;
14 import com.intellij.structuralsearch.impl.matcher.MatcherImplUtil;
15 import com.intellij.structuralsearch.impl.matcher.PatternTreeContext;
16 import com.intellij.structuralsearch.impl.matcher.predicates.ScriptSupport;
17 import com.intellij.structuralsearch.plugin.replace.ReplaceOptions;
18 import com.intellij.util.IncorrectOperationException;
19 import org.jetbrains.annotations.NotNull;
20 import org.jetbrains.annotations.Nullable;
21
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26
27 /**
28  * @author maxim
29  * Date: 24.02.2004
30  * Time: 15:34:57
31  */
32 public final class ReplacementBuilder {
33   private final String replacement;
34   private final List<ParameterInfo> parameterizations = new ArrayList<ParameterInfo>();
35   private final Map<String, ScriptSupport> replacementVarsMap;
36   private final ReplaceOptions options;
37   private final Project myProject;
38
39   ReplacementBuilder(final Project project,final ReplaceOptions options) {
40     myProject = project;
41     replacementVarsMap = new HashMap<String, ScriptSupport>();
42     this.options = options;
43     String _replacement = options.getReplacement();
44     FileType fileType = options.getMatchOptions().getFileType();
45
46     final Template template = TemplateManager.getInstance(project).createTemplate("","",_replacement);
47
48     final int segmentsCount = template.getSegmentsCount();
49     replacement = template.getTemplateText();
50
51     for(int i=0;i<segmentsCount;++i) {
52       final int offset = template.getSegmentOffset(i);
53       final String name = template.getSegmentName(i);
54
55       final ParameterInfo info = new ParameterInfo();
56       info.setStartIndex(offset);
57       info.setName(name);
58       info.setReplacementVariable(options.getVariableDefinition(name) != null);
59
60       // find delimiter
61       int pos;
62       for(pos = offset-1; pos >=0 && pos < replacement.length() && Character.isWhitespace(replacement.charAt(pos));) {
63         --pos;
64       }
65
66       if (pos >= 0) {
67         if (replacement.charAt(pos) == ',') {
68           info.setHasCommaBefore(true);
69         }
70         info.setBeforeDelimiterPos(pos);
71       }
72
73       for(pos = offset; pos < replacement.length() && Character.isWhitespace(replacement.charAt(pos));) {
74         ++pos;
75       }
76
77       if (pos < replacement.length()) {
78         final char ch = replacement.charAt(pos);
79
80         if (ch == ';') {
81           info.setStatementContext(true);
82         }
83         else if (ch == ',' || ch == ')') {
84           info.setArgumentContext(true);
85           info.setHasCommaAfter(ch == ',');
86         }
87       }
88       info.setAfterDelimiterPos(pos);
89
90       parameterizations.add(info);
91     }
92
93     final StructuralSearchProfile profile = parameterizations != null ? StructuralSearchUtil.getProfileByFileType(fileType) : null;
94     if (profile != null) {
95       try {
96         final PsiElement[] elements = MatcherImplUtil.createTreeFromText(
97           _replacement,
98           PatternTreeContext.Block,
99           fileType,
100           options.getMatchOptions().getDialect(),
101           options.getMatchOptions().getPatternContext(),
102           project,
103           false
104         );
105         if (elements.length > 0) {
106           final PsiElement patternNode = elements[0].getParent();
107           patternNode.accept(new PsiRecursiveElementWalkingVisitor() {
108             @Override
109             public void visitElement(PsiElement element) {
110               super.visitElement(element);
111               final String text = element.getText();
112               if (StructuralSearchUtil.isTypedVariable(text)) {
113                 final ParameterInfo parameterInfo = findParameterization(Replacer.stripTypedVariableDecoration(text));
114                 if (parameterInfo != null && parameterInfo.getElement() == null) {
115                   parameterInfo.setElement(element);
116                 }
117               }
118             }
119           });
120           profile.provideAdditionalReplaceOptions(patternNode, options, this);
121         }
122       } catch (IncorrectOperationException e) {
123         throw new MalformedPatternException();
124       }
125     }
126   }
127
128   private static void fill(MatchResult r,Map<String,MatchResult> m) {
129     if (r.getName()!=null) {
130       if (m.get(r.getName()) == null) {
131         m.put(r.getName(), r);
132       }
133     }
134
135     if (!r.isScopeMatch() || !r.isMultipleMatch()) {
136       for (final MatchResult matchResult : r.getAllSons()) {
137         fill(matchResult, m);
138       }
139     } else if (r.hasSons()) {
140       final List<MatchResult> allSons = r.getAllSons();
141       if (allSons.size() > 0) {
142         fill(allSons.get(0),m);
143       }
144     }
145   }
146
147   String process(MatchResult match, ReplacementInfoImpl replacementInfo, FileType type) {
148     if (parameterizations==null) {
149       return replacement;
150     }
151
152     final StringBuilder result = new StringBuilder(replacement);
153     final HashMap<String, MatchResult> matchMap = new HashMap<String, MatchResult>();
154     fill(match, matchMap);
155
156     final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByFileType(type);
157     assert profile != null;
158
159     int offset = 0;
160     for (final ParameterInfo info : parameterizations) {
161       MatchResult r = matchMap.get(info.getName());
162       if (info.isReplacementVariable()) {
163         offset = Replacer.insertSubstitution(result, offset, info, generateReplacement(info, match));
164       }
165       else if (r != null) {
166         offset = profile.handleSubstitution(info, r, result, offset, matchMap);
167       }
168       else {
169         offset = profile.handleNoSubstitution(info, offset, result);
170       }
171     }
172
173     replacementInfo.variableMap = matchMap;
174     return result.toString();
175   }
176
177   private String generateReplacement(ParameterInfo info, MatchResult match) {
178     ScriptSupport scriptSupport = replacementVarsMap.get(info.getName());
179
180     if (scriptSupport == null) {
181       String constraint = options.getVariableDefinition(info.getName()).getScriptCodeConstraint();
182       scriptSupport = new ScriptSupport(myProject, StringUtil.stripQuotesAroundValue(constraint), info.getName());
183       replacementVarsMap.put(info.getName(), scriptSupport);
184     }
185     return scriptSupport.evaluate(match, null);
186   }
187
188   @Nullable
189   public ParameterInfo findParameterization(String name) {
190     for (final ParameterInfo info : parameterizations) {
191       if (info.getName().equals(name)) {
192         return info;
193       }
194     }
195
196     return null;
197   }
198
199   public void addParametrization(@NotNull ParameterInfo e) {
200     parameterizations.add(e);
201   }
202 }