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