cleanup, use warn instead of info to report read error
[idea/community.git] / platform / lang-impl / src / com / intellij / codeInsight / template / impl / TemplateImpl.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.intellij.codeInsight.template.impl;
18
19 import com.intellij.codeInsight.template.Expression;
20 import com.intellij.codeInsight.template.Template;
21 import com.intellij.openapi.extensions.Extensions;
22 import com.intellij.openapi.options.SchemeElement;
23 import com.intellij.openapi.util.text.StringUtil;
24 import com.intellij.psi.tree.IElementType;
25 import com.intellij.util.SmartList;
26 import org.jetbrains.annotations.NonNls;
27 import org.jetbrains.annotations.NotNull;
28 import org.jetbrains.annotations.Nullable;
29
30 import java.util.*;
31
32 public class TemplateImpl extends Template implements SchemeElement {
33   private String myKey;
34   @NotNull private String myString;
35   private String myDescription;
36   private String myGroupName;
37   private char myShortcutChar = TemplateSettings.DEFAULT_CHAR;
38   private final List<Variable> myVariables = new SmartList<>();
39   private List<Segment> mySegments = null;
40   private String myTemplateText = null;
41   private String myId;
42
43   public boolean equals(Object o) {
44     if (this == o) return true;
45     if (!(o instanceof TemplateImpl)) return false;
46
47     final TemplateImpl template = (TemplateImpl) o;
48     if (myId != null && template.myId != null && myId.equals(template.myId)) return true;
49
50     if (isToReformat != template.isToReformat) return false;
51     if (isToShortenLongNames != template.isToShortenLongNames) return false;
52     if (myShortcutChar != template.myShortcutChar) return false;
53     if (myDescription != null ? !myDescription.equals(template.myDescription) : template.myDescription != null) return false;
54     if (myGroupName != null ? !myGroupName.equals(template.myGroupName) : template.myGroupName != null) return false;
55     if (myKey != null ? !myKey.equals(template.myKey) : template.myKey != null) return false;
56     if (!myString.equals(template.myString)) return false;
57     if (myTemplateText != null ? !myTemplateText.equals(template.myTemplateText) : template.myTemplateText != null) return false;
58
59     if (!new HashSet<>(myVariables).equals(new HashSet<>(template.myVariables))) return false;
60     if (isDeactivated != template.isDeactivated) return false;
61
62     return true;
63   }
64
65   public int hashCode() {
66     if (myId != null) {
67       return myId.hashCode();
68     }
69     int result;
70     result = myKey.hashCode();
71     result = 29 * result + myString.hashCode();
72     result = 29 * result + myGroupName.hashCode();
73     return result;
74   }
75
76   private boolean isToReformat = false;
77   private boolean isToShortenLongNames = true;
78   private boolean toParseSegments = true;
79   private TemplateContext myTemplateContext = new TemplateContext();
80
81   @NonNls public static final String END = "END";
82   @NonNls public static final String SELECTION = "SELECTION";
83   @NonNls public static final String SELECTION_START = "SELECTION_START";
84   @NonNls public static final String SELECTION_END = "SELECTION_END";
85   @NonNls public static final String ARG = "ARG";
86
87   public static final Set<String> INTERNAL_VARS_SET = new HashSet<>(Arrays.asList(
88     END, SELECTION, SELECTION_START, SELECTION_END));
89
90   private boolean isDeactivated = false;
91
92   public boolean isInline() {
93     return myIsInline;
94   }
95
96   private boolean isToIndent = true;
97
98
99   @Override
100   public void setInline(boolean isInline) {
101     myIsInline = isInline;
102   }
103
104   private boolean myIsInline = false;
105
106   public TemplateImpl(@NotNull String key, @NotNull String group) {
107     this(key, null, group);
108     toParseSegments = false;
109     myTemplateText = "";
110     mySegments = new SmartList<>();
111   }
112
113   public TemplateImpl(@NotNull String key, String string, @NotNull String group) {
114     myKey = key;
115     myString = StringUtil.convertLineSeparators(StringUtil.notNullize(string));
116     myGroupName = group;
117   }
118
119   @Override
120   public void addTextSegment(@NotNull String text) {
121     text = StringUtil.convertLineSeparators(text);
122     myTemplateText += text;
123   }
124
125   @Override
126   public void addVariableSegment(@NotNull String name) {
127     mySegments.add(new Segment(name, myTemplateText.length()));
128   }
129
130   @Override
131   public Variable addVariable(Expression expression, boolean isAlwaysStopAt) {
132     return addVariable("__Variable" + myVariables.size(), expression, isAlwaysStopAt);
133   }
134
135   @Override
136   public Variable addVariable(@NotNull String name,
137                               Expression expression,
138                               Expression defaultValueExpression,
139                               boolean isAlwaysStopAt,
140                               boolean skipOnStart) {
141     if (mySegments != null) {
142       Segment segment = new Segment(name, myTemplateText.length());
143       mySegments.add(segment);
144     }
145     Variable variable = new Variable(name, expression, defaultValueExpression, isAlwaysStopAt, skipOnStart);
146     myVariables.add(variable);
147     return variable;
148   }
149
150   @Override
151   public void addEndVariable() {
152     Segment segment = new Segment(END, myTemplateText.length());
153     mySegments.add(segment);
154   }
155
156   @Override
157   public void addSelectionStartVariable() {
158     Segment segment = new Segment(SELECTION_START, myTemplateText.length());
159     mySegments.add(segment);
160   }
161
162   @Override
163   public void addSelectionEndVariable() {
164     Segment segment = new Segment(SELECTION_END, myTemplateText.length());
165     mySegments.add(segment);
166   }
167
168   @Override
169   public String getId() {
170     return myId;
171   }
172
173   @NotNull
174   @Override
175   public TemplateImpl copy() {
176     TemplateImpl template = new TemplateImpl(myKey, myString, myGroupName);
177     template.resetFrom(this);
178     return template;
179   }
180
181   public void resetFrom(TemplateImpl another) {
182     removeAllParsed();
183     toParseSegments = another.toParseSegments;
184
185     myKey = another.getKey();
186     myString = another.myString;
187     myTemplateText = another.myTemplateText;
188     myGroupName = another.myGroupName;
189     myId = another.myId;
190     myDescription = another.myDescription;
191     myShortcutChar = another.myShortcutChar;
192     isToReformat = another.isToReformat;
193     isToShortenLongNames = another.isToShortenLongNames;
194     myIsInline = another.myIsInline;
195     myTemplateContext = another.myTemplateContext.createCopy();
196     isDeactivated = another.isDeactivated;
197     for (Property property : Property.values()) {
198       boolean value = another.getValue(property);
199       if (value != Template.getDefaultValue(property)) {
200         setValue(property, true);
201       }
202     }
203     for (Variable variable : another.myVariables) {
204       addVariable(variable.getName(), variable.getExpressionString(), variable.getDefaultValueString(), variable.isAlwaysStopAt());
205     }
206   }
207
208   @Override
209   public boolean isToReformat() {
210     return isToReformat;
211   }
212
213   @Override
214   public void setToReformat(boolean toReformat) {
215     isToReformat = toReformat;
216   }
217
218   @Override
219   public void setToIndent(boolean toIndent) {
220     isToIndent = toIndent;
221   }
222
223   public boolean isToIndent() {
224     return isToIndent;
225   }
226
227   @Override
228   public boolean isToShortenLongNames() {
229     return isToShortenLongNames;
230   }
231
232   @Override
233   public void setToShortenLongNames(boolean toShortenLongNames) {
234     isToShortenLongNames = toShortenLongNames;
235   }
236
237   public void setDeactivated(boolean isDeactivated) {
238     this.isDeactivated = isDeactivated;
239   }
240
241   public boolean isDeactivated() {
242     return isDeactivated;
243   }
244
245   @NotNull
246   public TemplateContext getTemplateContext() {
247     return myTemplateContext;
248   }
249
250   public int getEndSegmentNumber() {
251     return getVariableSegmentNumber(END);
252   }
253
254   public int getSelectionStartSegmentNumber() {
255     return getVariableSegmentNumber(SELECTION_START);
256   }
257
258   public int getSelectionEndSegmentNumber() {
259     return getVariableSegmentNumber(SELECTION_END);
260   }
261
262   public int getVariableSegmentNumber(String variableName) {
263     parseSegments();
264     for (int i = 0; i < mySegments.size(); i++) {
265       Segment segment = mySegments.get(i);
266       if (segment.name.equals(variableName)) {
267         return i;
268       }
269     }
270     return -1;
271   }
272
273   @NotNull
274   @Override
275   public String getTemplateText() {
276     parseSegments();
277     return myTemplateText;
278   }
279
280   @Override
281   public String getSegmentName(int i) {
282     parseSegments();
283     return mySegments.get(i).name;
284   }
285
286   @Override
287   public int getSegmentOffset(int i) {
288     parseSegments();
289     return mySegments.get(i).offset;
290   }
291
292   @Override
293   public int getSegmentsCount() {
294     parseSegments();
295     return mySegments.size();
296   }
297
298   public void parseSegments() {
299     if(!toParseSegments) {
300       return;
301     }
302     if(mySegments != null) {
303       return;
304     }
305
306     mySegments = new SmartList<>();
307     StringBuilder buffer = new StringBuilder("");
308     TemplateTextLexer lexer = new TemplateTextLexer();
309     lexer.start(myString);
310
311     while(true){
312       IElementType tokenType = lexer.getTokenType();
313       if (tokenType == null) break;
314       int start = lexer.getTokenStart();
315       int end = lexer.getTokenEnd();
316       String token = myString.substring(start, end);
317       if (tokenType == TemplateTokenType.VARIABLE){
318         String name = token.substring(1, token.length() - 1);
319         Segment segment = new Segment(name, buffer.length());
320         mySegments.add(segment);
321       }
322       else if (tokenType == TemplateTokenType.ESCAPE_DOLLAR){
323         buffer.append("$");
324       }
325       else{
326         buffer.append(token);
327       }
328       lexer.advance();
329     }
330     myTemplateText = buffer.toString();
331   }
332
333   public void removeAllParsed() {
334     myVariables.clear();
335     mySegments = null;
336   }
337
338   @Override
339   public Variable addVariable(@NotNull String name, String expression, String defaultValue, boolean isAlwaysStopAt) {
340     Variable variable = new Variable(name, expression, defaultValue, isAlwaysStopAt);
341     myVariables.add(variable);
342     return variable;
343   }
344
345   public void removeVariable(int i) {
346     myVariables.remove(i);
347   }
348
349   public int getVariableCount() {
350     return myVariables.size();
351   }
352
353   public String getVariableNameAt(int i) {
354     return myVariables.get(i).getName();
355   }
356
357   public String getExpressionStringAt(int i) {
358     return myVariables.get(i).getExpressionString();
359   }
360
361   public Expression getExpressionAt(int i) {
362     return myVariables.get(i).getExpression();
363   }
364
365   public String getDefaultValueStringAt(int i) {
366     return myVariables.get(i).getDefaultValueString();
367   }
368
369   public Expression getDefaultValueAt(int i) {
370     return myVariables.get(i).getDefaultValueExpression();
371   }
372
373   public boolean isAlwaysStopAt(int i) {
374     return myVariables.get(i).isAlwaysStopAt();
375   }
376
377   @Override
378   public String getKey() {
379     return myKey;
380   }
381
382   public void setKey(String key) {
383     myKey = key;
384   }
385
386   @NotNull
387   @Override
388   public String getString() {
389     parseSegments();
390     return myString;
391   }
392
393   /**
394    * Set template text as it appears in Live Template settings, including variables surrounded with '$'.
395    * The text will be reparsed when needed.
396    * @param string template string text
397    */
398   public void setString(@NotNull String string) {
399     myString = StringUtil.convertLineSeparators(string);
400     toParseSegments = true;
401   }
402
403   @Override
404   public String getDescription() {
405     return myDescription;
406   }
407
408   public void setDescription(String description) {
409     myDescription = description;
410   }
411
412   public char getShortcutChar() {
413     return myShortcutChar;
414   }
415
416   public void setShortcutChar(char shortcutChar) {
417     myShortcutChar = shortcutChar;
418   }
419
420   public String getGroupName() {
421     return myGroupName;
422   }
423
424   @Override
425   public void setGroupName(@NotNull String groupName) {
426     myGroupName = groupName;
427   }
428
429   public boolean isSelectionTemplate() {
430     parseSegments();
431     for (Segment v : mySegments) {
432       if (SELECTION.equals(v.name)) return true;
433     }
434
435     return false;
436   }
437
438   public boolean hasArgument() {
439     for (Variable v : myVariables) {
440       if (v.getName().equals(ARG)) return true;
441     }
442     return false;
443   }
444
445   public void setId(@Nullable final String id) {
446     myId = id;
447   }
448
449   public Map<TemplateOptionalProcessor, Boolean> createOptions() {
450     Map<TemplateOptionalProcessor, Boolean> context = new LinkedHashMap<>();
451     for (TemplateOptionalProcessor processor : Extensions.getExtensions(TemplateOptionalProcessor.EP_NAME)) {
452       context.put(processor, processor.isEnabled(this));
453     }
454     return context;
455   }
456
457   public TemplateContext createContext() {
458     return getTemplateContext().createCopy();
459   }
460
461   public boolean contextsEqual(TemplateImpl defaultTemplate) {
462     return getTemplateContext().getDifference(defaultTemplate.getTemplateContext()) == null;
463   }
464
465   public void applyOptions(final Map<TemplateOptionalProcessor, Boolean> context) {
466     for (Map.Entry<TemplateOptionalProcessor, Boolean> entry : context.entrySet()) {
467       entry.getKey().setEnabled(this, entry.getValue().booleanValue());
468     }
469   }
470
471   public void applyContext(final TemplateContext context) {
472     myTemplateContext = context.createCopy();
473   }
474
475   public boolean skipOnStart(int i) {
476     return myVariables.get(i).skipOnStart();
477   }
478
479   public ArrayList<Variable> getVariables() {
480     return new ArrayList<>(myVariables);
481   }
482
483   private static class Segment {
484     public final String name;
485     public final int offset;
486
487     private Segment(@NotNull String name, int offset) {
488       this.name = name;
489       this.offset = offset;
490     }
491   }
492
493   @Override
494   public String toString() {
495     return myGroupName +"/" + myKey;
496   }
497 }