SSR: add read action
[idea/community.git] / platform / structuralsearch / source / com / intellij / structuralsearch / impl / matcher / MatcherImpl.java
1 package com.intellij.structuralsearch.impl.matcher;
2
3 import com.intellij.dupLocator.iterators.ArrayBackedNodeIterator;
4 import com.intellij.dupLocator.iterators.NodeIterator;
5 import com.intellij.lang.Language;
6 import com.intellij.lang.StdLanguages;
7 import com.intellij.openapi.application.ApplicationManager;
8 import com.intellij.openapi.application.ModalityState;
9 import com.intellij.openapi.diagnostic.Logger;
10 import com.intellij.openapi.editor.Document;
11 import com.intellij.openapi.fileTypes.FileType;
12 import com.intellij.openapi.fileTypes.FileTypes;
13 import com.intellij.openapi.fileTypes.LanguageFileType;
14 import com.intellij.openapi.progress.ProcessCanceledException;
15 import com.intellij.openapi.progress.ProgressIndicator;
16 import com.intellij.openapi.project.Project;
17 import com.intellij.openapi.roots.ContentIterator;
18 import com.intellij.openapi.util.Computable;
19 import com.intellij.openapi.util.Pair;
20 import com.intellij.openapi.vfs.VirtualFile;
21 import com.intellij.psi.*;
22 import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
23 import com.intellij.psi.search.GlobalSearchScope;
24 import com.intellij.psi.search.LocalSearchScope;
25 import com.intellij.psi.search.SearchScope;
26 import com.intellij.structuralsearch.*;
27 import com.intellij.structuralsearch.impl.matcher.compiler.PatternCompiler;
28 import com.intellij.structuralsearch.impl.matcher.handlers.MatchingHandler;
29 import com.intellij.structuralsearch.impl.matcher.handlers.TopLevelMatchingHandler;
30 import com.intellij.structuralsearch.impl.matcher.iterators.SsrFilteringNodeIterator;
31 import com.intellij.structuralsearch.impl.matcher.strategies.MatchingStrategy;
32 import com.intellij.structuralsearch.plugin.ui.Configuration;
33 import com.intellij.structuralsearch.plugin.util.CollectingMatchResultSink;
34 import com.intellij.util.IncorrectOperationException;
35 import com.intellij.util.PairProcessor;
36 import com.intellij.util.SmartList;
37 import com.intellij.util.indexing.FileBasedIndex;
38 import org.jetbrains.annotations.NotNull;
39 import org.jetbrains.annotations.Nullable;
40
41 import java.lang.ref.SoftReference;
42 import java.util.ArrayList;
43 import java.util.LinkedList;
44 import java.util.List;
45
46 /**
47  * This class makes program structure tree matching:
48  */
49 public class MatcherImpl {
50   private static final Logger LOG = Logger.getInstance("#com.intellij.structuralsearch.impl.matcher.MatcherImpl");
51   // project being worked on
52   private final Project project;
53
54   // context of matching
55   private final MatchContext matchContext;
56   private boolean isTesting;
57
58   // visitor to delegate the real work
59   private final GlobalMatchingVisitor visitor = new GlobalMatchingVisitor();
60   private ProgressIndicator progress;
61   private final TaskScheduler scheduler = new TaskScheduler();
62
63   private int totalFilesToScan;
64   private int scannedFilesCount;
65
66   public MatcherImpl(final Project project, final MatchOptions matchOptions) {
67     this.project = project;
68     matchContext = new MatchContext();
69     matchContext.setMatcher(visitor);
70
71     if (matchOptions != null) {
72       matchContext.setOptions(matchOptions);
73       cacheCompiledPattern(matchOptions, PatternCompiler.compilePattern(project,matchOptions));
74     }
75   }
76
77   static class LastMatchData {
78     CompiledPattern lastPattern;
79     MatchOptions lastOptions;
80   }
81
82   private static SoftReference<LastMatchData> lastMatchData;
83
84   protected MatcherImpl(Project project) {
85     this(project, null);
86   }
87
88   public static void validate(Project project, MatchOptions options) {
89     PsiDocumentManager.getInstance(project).commitAllDocuments();
90
91     synchronized(MatcherImpl.class) {
92       final LastMatchData data = new LastMatchData();
93       data.lastPattern =  PatternCompiler.compilePattern(project, options);
94       data.lastOptions = options;
95       lastMatchData = new SoftReference<LastMatchData>(data);
96     }
97
98     final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByFileType(options.getFileType());
99     profile.checkSearchPattern(project, options);
100   }
101
102   public static class CompiledOptions {
103     public final List<Pair<MatchContext, Configuration>> matchContexts;
104
105     public CompiledOptions(final List<Pair<MatchContext, Configuration>> matchContexts) {
106       this.matchContexts = matchContexts;
107     }
108
109     public List<Pair<MatchContext, Configuration>> getMatchContexts() {
110       return matchContexts;
111     }
112   }
113
114   public static boolean checkIfShouldAttemptToMatch(MatchContext context, NodeIterator matchedNodes) {
115     final CompiledPattern pattern = context.getPattern();
116     final NodeIterator patternNodes = pattern.getNodes();
117     try {
118       while (true) {
119         final PsiElement patternNode = patternNodes.current();
120         if (patternNode == null) {
121           return true;
122         }
123         final PsiElement matchedNode = matchedNodes.current();
124         if (matchedNode == null) {
125           return false;
126         }
127         final MatchingHandler matchingHandler = pattern.getHandler(patternNode);
128         if (matchingHandler == null || !matchingHandler.canMatch(patternNode, matchedNode)) {
129           return false;
130         }
131         matchedNodes.advance();
132         patternNodes.advance();
133       }
134     } finally {
135       patternNodes.reset();
136       matchedNodes.reset();
137     }
138   }
139
140   public void processMatchesInElement(MatchContext context, Configuration configuration,
141                                       NodeIterator matchedNodes,
142                                       PairProcessor<MatchResult, Configuration> processor) {
143     try {
144       configureOptions(context, configuration, matchedNodes.current(), processor);
145       context.setShouldRecursivelyMatch(false);
146       visitor.matchContext(matchedNodes);
147     } finally {
148       matchedNodes.reset();
149     }
150   }
151
152   public void clearContext() {
153     matchContext.clear();
154   }
155
156   private void configureOptions(MatchContext context,
157                                 final Configuration configuration,
158                                 PsiElement psiFile,
159                                 final PairProcessor<MatchResult, Configuration> processor) {
160     if (psiFile == null) return;
161     LocalSearchScope scope = new LocalSearchScope(psiFile);
162
163     matchContext.clear();
164     matchContext.setMatcher(visitor);
165
166     MatchOptions options = context.getOptions();
167     matchContext.setOptions(options);
168     matchContext.setPattern(context.getPattern());
169     matchContext.setShouldRecursivelyMatch(context.shouldRecursivelyMatch());
170     visitor.setMatchContext(matchContext);
171
172     matchContext.setSink(
173       new MatchConstraintsSink(
174         new MatchResultSink() {
175           public void newMatch(MatchResult result) {
176             processor.process(result, configuration);
177           }
178
179           public void processFile(PsiFile element) {
180           }
181
182           public void setMatchingProcess(MatchingProcess matchingProcess) {
183           }
184
185           public void matchingFinished() {
186           }
187
188           public ProgressIndicator getProgressIndicator() {
189             return null;
190           }
191         },
192         options.getMaxMatchesCount(),
193         options.isDistinct(),
194         options.isCaseSensitiveMatch()
195       )
196     );
197     options.setScope(scope);
198   }
199
200   public CompiledOptions precompileOptions(List<Configuration> configurations) {
201     final List<Pair<MatchContext, Configuration>> contexts = new ArrayList<Pair<MatchContext, Configuration>>();
202
203     for (final Configuration configuration : configurations) {
204       final MatchContext matchContext = new MatchContext();
205       matchContext.setMatcher(visitor);
206       final MatchOptions matchOptions = configuration.getMatchOptions();
207       matchContext.setOptions(matchOptions);
208
209       ApplicationManager.getApplication().runReadAction(new Runnable() {
210         @Override
211         public void run() {
212           try {
213             CompiledPattern compiledPattern = PatternCompiler.compilePattern(project, matchOptions);
214             matchContext.setPattern(compiledPattern);
215             contexts.add(Pair.create(matchContext, configuration));
216           }
217           catch (UnsupportedPatternException ignored) {}
218           catch (MalformedPatternException ignored) {}
219         }
220       });
221     }
222     return new CompiledOptions(contexts);
223   }
224
225   Project getProject() {
226     return project;
227   }
228
229   /**
230    * Finds the matches of given pattern starting from given tree element.
231    * @throws MalformedPatternException
232    * @throws UnsupportedPatternException
233    */
234   protected void findMatches(MatchResultSink sink, final MatchOptions options) throws MalformedPatternException, UnsupportedPatternException
235   {
236     CompiledPattern compiledPattern = prepareMatching(sink, options);
237     if (compiledPattern== null) {
238       return;
239     }
240
241     matchContext.getSink().setMatchingProcess( scheduler );
242     scheduler.init();
243     progress = matchContext.getSink().getProgressIndicator();
244
245     if (/*TokenBasedSearcher.canProcess(compiledPattern)*/ false) {
246       //TokenBasedSearcher searcher = new TokenBasedSearcher(this);
247       //searcher.search(compiledPattern);
248       if (isTesting) {
249         matchContext.getSink().matchingFinished();
250         return;
251       }
252     }
253     else {
254       if (isTesting) {
255         // testing mode;
256         final PsiElement[] elements = ((LocalSearchScope)options.getScope()).getScope();
257
258         PsiElement parent = elements[0].getParent();
259         if (elements.length > 0 && matchContext.getPattern().getStrategy().continueMatching(parent != null ? parent : elements[0])) {
260           visitor.matchContext(new SsrFilteringNodeIterator(new ArrayBackedNodeIterator(elements)));
261         }
262         else {
263           for (PsiElement element : elements) {
264             match(element);
265           }
266         }
267
268         matchContext.getSink().matchingFinished();
269         return;
270       }
271       if (!findMatches(options, compiledPattern)) {
272         return;
273       }
274     }
275
276     if (scheduler.getTaskQueueEndAction()==null) {
277       scheduler.setTaskQueueEndAction(
278         new Runnable() {
279           public void run() {
280             matchContext.getSink().matchingFinished();
281           }
282         }
283       );
284     }
285
286     scheduler.executeNext();
287   }
288
289   private boolean findMatches(MatchOptions options, CompiledPattern compiledPattern) {
290     LanguageFileType languageFileType = (LanguageFileType)options.getFileType();
291     final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByLanguage(languageFileType.getLanguage());
292     assert profile != null;
293     PsiElement node = compiledPattern.getNodes().current();
294     final Language ourPatternLanguage = node != null ? profile.getLanguage(node) : ((LanguageFileType)options.getFileType()).getLanguage();
295     final Language ourPatternLanguage2 = ourPatternLanguage == StdLanguages.XML ? StdLanguages.XHTML:null;
296     SearchScope searchScope = compiledPattern.getScope();
297     boolean ourOptimizedScope = searchScope != null;
298     if (!ourOptimizedScope) searchScope = options.getScope();
299
300     if (searchScope instanceof GlobalSearchScope) {
301       final GlobalSearchScope scope = (GlobalSearchScope)searchScope;
302
303       final ContentIterator ci = new ContentIterator() {
304         public boolean processFile(final VirtualFile fileOrDir) {
305           if (!fileOrDir.isDirectory() && scope.contains(fileOrDir) && fileOrDir.getFileType() != FileTypes.UNKNOWN) {
306             ++totalFilesToScan;
307             scheduler.addOneTask(new MatchOneVirtualFile(fileOrDir, profile, ourPatternLanguage, ourPatternLanguage2));
308           }
309           return true;
310         }
311       };
312
313       ApplicationManager.getApplication().runReadAction(new Runnable() {
314         @Override
315         public void run() {
316           FileBasedIndex.getInstance().iterateIndexableFiles(ci, project, progress);
317         }
318       });
319       progress.setText2("");
320     }
321     else {
322       final PsiElement[] elementsToScan = ((LocalSearchScope)searchScope).getScope();
323       totalFilesToScan = elementsToScan.length;
324
325       for (int i = 0; i < elementsToScan.length; ++i) {
326         final PsiElement psiElement = elementsToScan[i];
327
328         if (psiElement == null) continue;
329         final Language language = psiElement.getLanguage();
330
331         PsiFile file = psiElement instanceof PsiFile ? (PsiFile)psiElement : psiElement.getContainingFile();
332
333         if (profile.isMyFile(file, language, ourPatternLanguage, ourPatternLanguage2)) {
334           scheduler.addOneTask(new MatchOnePsiFile(psiElement));
335         }
336         if (ourOptimizedScope) elementsToScan[i] = null; // to prevent long PsiElement reference
337       }
338     }
339     return true;
340   }
341
342   private CompiledPattern prepareMatching(final MatchResultSink sink, final MatchOptions options) {
343     CompiledPattern savedPattern = null;
344
345     if (matchContext.getOptions() == options && matchContext.getPattern() != null &&
346         matchContext.getOptions().hashCode() == matchContext.getPattern().getOptionsHashStamp()) {
347       savedPattern = matchContext.getPattern();
348     }
349
350     matchContext.clear();
351     matchContext.setSink(
352       new MatchConstraintsSink(
353         sink,
354         options.getMaxMatchesCount(),
355         options.isDistinct(),
356         options.isCaseSensitiveMatch()
357       )
358     );
359     matchContext.setOptions(options);
360     matchContext.setMatcher(visitor);
361     visitor.setMatchContext(matchContext);
362
363     CompiledPattern compiledPattern = savedPattern;
364
365     if (compiledPattern == null) {
366
367       synchronized(getClass()) {
368         final LastMatchData data = com.intellij.reference.SoftReference.dereference(lastMatchData);
369         if (data != null && options == data.lastOptions) {
370           compiledPattern = data.lastPattern;
371         }
372         lastMatchData = null;
373       }
374
375       if (compiledPattern==null) {
376         compiledPattern = ApplicationManager.getApplication().runReadAction(new Computable<CompiledPattern>() {
377           @Override
378           public CompiledPattern compute() {
379             return PatternCompiler.compilePattern(project,options);
380           }
381         });
382       }
383     }
384
385     cacheCompiledPattern(options, compiledPattern);
386     return compiledPattern;
387   }
388
389   private void cacheCompiledPattern(final MatchOptions options, final CompiledPattern compiledPattern) {
390     matchContext.setPattern(compiledPattern);
391     compiledPattern.setOptionsHashStamp(options.hashCode());
392   }
393
394   /**
395    * Finds the matches of given pattern starting from given tree element.
396    * @param sink match result destination
397    * @throws MalformedPatternException
398    * @throws UnsupportedPatternException
399    */
400   protected void testFindMatches(MatchResultSink sink, MatchOptions options)
401     throws MalformedPatternException, UnsupportedPatternException {
402     isTesting = true;
403     try {
404       findMatches(sink,options);
405     } finally {
406       isTesting = false;
407     }
408   }
409
410   /**
411    * Finds the matches of given pattern starting from given tree element.
412    * @param source string for search
413    * @param pattern to be searched
414    * @return list of matches found
415    * @throws MalformedPatternException
416    * @throws UnsupportedPatternException
417    */
418   protected List<MatchResult> testFindMatches(String source,
419                                               String pattern,
420                                               MatchOptions options,
421                                               boolean filePattern,
422                                               FileType sourceFileType,
423                                               String sourceExtension,
424                                               boolean physicalSourceFile)
425     throws MalformedPatternException, UnsupportedPatternException {
426
427     CollectingMatchResultSink sink = new CollectingMatchResultSink();
428
429     try {
430       PsiElement[] elements = MatcherImplUtil.createSourceTreeFromText(source,
431                                                                        filePattern ? PatternTreeContext.File : PatternTreeContext.Block,
432                                                                        sourceFileType,
433                                                                        sourceExtension,
434                                                                        project, physicalSourceFile);
435
436       options.setSearchPattern(pattern);
437       options.setScope(new LocalSearchScope(elements));
438       testFindMatches(sink, options);
439     }
440     catch (IncorrectOperationException e) {
441       MalformedPatternException exception = new MalformedPatternException();
442       exception.initCause(e);
443       throw exception;
444     }
445
446     return sink.getMatches();
447   }
448
449   protected List<MatchResult> testFindMatches(String source, String pattern, MatchOptions options, boolean filePattern) {
450     return testFindMatches(source, pattern, options, filePattern, options.getFileType(), null, false);
451   }
452
453   class TaskScheduler implements MatchingProcess {
454     private LinkedList<Runnable> tasks = new LinkedList<Runnable>();
455     private boolean ended;
456     private Runnable taskQueueEndAction;
457
458     private boolean suspended;
459
460     public void stop() {
461       ended = true;
462     }
463
464     public void pause() {
465       suspended = true;
466     }
467
468     public void resume() {
469       if (!suspended) return;
470       suspended = false;
471       executeNext();
472     }
473
474     public boolean isSuspended() {
475       return suspended;
476     }
477
478     public boolean isEnded() {
479       return ended;
480     }
481
482     void setTaskQueueEndAction(Runnable taskQueueEndAction) {
483       this.taskQueueEndAction = taskQueueEndAction;
484     }
485     Runnable getTaskQueueEndAction () {
486       return taskQueueEndAction;
487     }
488
489     void addOneTask(Runnable runnable) {
490       tasks.add(runnable);
491     }
492
493     private void executeNext() {
494       while(!suspended && !ended) {
495         if (tasks.isEmpty()) {
496           ended = true;
497           break;
498         }
499
500         final Runnable task = tasks.removeFirst();
501         try {
502           task.run();
503         }
504         catch (ProcessCanceledException e) {
505           ended = true;
506           clearSchedule();
507           throw e;
508         }
509         catch (StructuralSearchException e) {
510           ended = true;
511           clearSchedule();
512           throw e;
513         }
514         catch (Throwable th) {
515           LOG.error(th);
516         }
517       }
518
519       if (ended) clearSchedule();
520     }
521
522     private void init() {
523       ended = false;
524       suspended = false;
525       PsiManager.getInstance(project).startBatchFilesProcessingMode();
526     }
527
528     private void clearSchedule() {
529       if (tasks != null) {
530         taskQueueEndAction.run();
531         if (!project.isDisposed()) {
532           PsiManager.getInstance(project).finishBatchFilesProcessingMode();
533         }
534         tasks = null;
535       }
536     }
537
538   }
539
540   private class MatchOnePsiFile extends MatchOneFile {
541     private PsiElement file;
542
543     MatchOnePsiFile(PsiElement file) {
544       this.file = file;
545     }
546
547     @Nullable
548     @Override
549     protected List<PsiElement> getPsiElementsToProcess() {
550       PsiElement file = this.file;
551       this.file = null;
552       return new SmartList<PsiElement>(file);
553     }
554   }
555
556   private abstract class MatchOneFile implements Runnable {
557     public void run() {
558       List<PsiElement> files = getPsiElementsToProcess();
559
560       if (progress!=null) {
561         progress.setFraction((double)scannedFilesCount/totalFilesToScan);
562       }
563
564       ++scannedFilesCount;
565
566       if (files == null || files.size() == 0) return;
567       final PsiFile psiFile = files.get(0).getContainingFile();
568
569       if (psiFile!=null) {
570         final Runnable action = new Runnable() {
571           public void run() {
572             ApplicationManager.getApplication().runWriteAction(new Runnable() {
573               public void run() {
574                 if (project.isDisposed()) return;
575                 final PsiDocumentManager manager = PsiDocumentManager.getInstance(project);
576                 Document document = manager.getDocument(psiFile);
577                 if (document != null) manager.commitDocument(document);
578               }
579             });
580           }
581         };
582
583         if (ApplicationManager.getApplication().isDispatchThread()) {
584           action.run();
585         } else {
586           ApplicationManager.getApplication().invokeAndWait(
587             action,
588             ModalityState.defaultModalityState()
589           );
590         }
591       }
592
593       if (project.isDisposed()) return;
594
595       for(PsiElement file:files) {
596         if (file instanceof PsiFile) {
597           matchContext.getSink().processFile((PsiFile)file);
598         }
599
600         final PsiElement finalFile = file;
601         ApplicationManager.getApplication().runReadAction(
602           new Runnable() {
603             public void run() {
604               PsiElement file = finalFile;
605               if (!file.isValid()) return;
606               file = StructuralSearchUtil.getProfileByLanguage(file.getLanguage()).extendMatchOnePsiFile(file);
607               match(file);
608             }
609           }
610         );
611       }
612     }
613
614     protected abstract @Nullable List<PsiElement> getPsiElementsToProcess();
615   }
616
617   // Initiates the matching process for given element
618   // @param element the current search tree element
619   public void match(PsiElement element) {
620     MatchingStrategy strategy = matchContext.getPattern().getStrategy();
621
622     if (strategy.continueMatching(element)) {
623       visitor.matchContext(new ArrayBackedNodeIterator(new PsiElement[] {element}));
624       return;
625     }
626     for(PsiElement el=element.getFirstChild();el!=null;el=el.getNextSibling()) {
627       match(el);
628     }
629     if (element instanceof PsiLanguageInjectionHost) {
630       InjectedLanguageUtil.enumerate(element, new PsiLanguageInjectionHost.InjectedPsiVisitor() {
631         @Override
632         public void visit(@NotNull PsiFile injectedPsi, @NotNull List<PsiLanguageInjectionHost.Shred> places) {
633           match(injectedPsi);
634         }
635       });
636     }
637   }
638
639   @Nullable
640   protected MatchResult isMatchedByDownUp(PsiElement element, final MatchOptions options) {
641     final CollectingMatchResultSink sink = new CollectingMatchResultSink();
642     CompiledPattern compiledPattern = prepareMatching(sink, options);
643
644     if (compiledPattern== null) {
645       assert false;
646       return null;
647     }
648
649     PsiElement targetNode = compiledPattern.getTargetNode();
650     PsiElement elementToStartMatching = null;
651
652     if (targetNode == null) {
653       targetNode = compiledPattern.getNodes().current();
654       if (targetNode != null) {
655         compiledPattern.getNodes().advance();
656         assert !compiledPattern.getNodes().hasNext();
657         compiledPattern.getNodes().rewind();
658
659         while (element.getClass() != targetNode.getClass()) {
660           element = element.getParent();
661           if (element == null)  return null;
662         }
663
664         elementToStartMatching = element;
665       }
666     } else {
667       targetNode = StructuralSearchUtil.getProfileByPsiElement(element).extendMatchedByDownUp(targetNode);
668
669       MatchingHandler handler = null;
670
671       while (element.getClass() == targetNode.getClass() ||
672              compiledPattern.isTypedVar(targetNode) && compiledPattern.getHandler(targetNode).canMatch(targetNode, element)
673             ) {
674         handler = compiledPattern.getHandler(targetNode);
675         handler.setPinnedElement(element);
676         elementToStartMatching = element;
677         if (handler instanceof TopLevelMatchingHandler) break;
678         element = element.getParent();
679         targetNode = targetNode.getParent();
680
681         if (options.isLooseMatching()) {
682           element = StructuralSearchUtil.getProfileByPsiElement(element).updateCurrentNode(element);
683           targetNode = StructuralSearchUtil.getProfileByPsiElement(element).updateCurrentNode(targetNode);
684         }
685       }
686
687       if (!(handler instanceof TopLevelMatchingHandler)) return null;
688     }
689
690     assert targetNode != null : "Could not match down up when no target node";
691
692     match(elementToStartMatching);
693     matchContext.getSink().matchingFinished();
694     final int matchCount = sink.getMatches().size();
695     assert matchCount <= 1;
696     return matchCount > 0 ? sink.getMatches().get(0) : null;
697   }
698
699   private class MatchOneVirtualFile extends MatchOneFile {
700     private final VirtualFile myFileOrDir;
701     private final StructuralSearchProfile myProfile;
702     private final Language myOurPatternLanguage;
703     private final Language myOurPatternLanguage2;
704
705     public MatchOneVirtualFile(VirtualFile fileOrDir,
706                                StructuralSearchProfile profile,
707                                Language ourPatternLanguage,
708                                Language ourPatternLanguage2) {
709       myFileOrDir = fileOrDir;
710       myProfile = profile;
711       myOurPatternLanguage = ourPatternLanguage;
712       myOurPatternLanguage2 = ourPatternLanguage2;
713     }
714
715     @Nullable
716     @Override
717     protected List<PsiElement> getPsiElementsToProcess() {
718       return ApplicationManager.getApplication().runReadAction(new Computable<List<PsiElement>>() {
719         @Override
720         public List<PsiElement> compute() {
721           PsiFile file = PsiManager.getInstance(project).findFile(myFileOrDir);
722           if (file == null) {
723             return null;
724           }
725
726           final FileViewProvider viewProvider = file.getViewProvider();
727           List<PsiElement> elementsToProcess = new SmartList<PsiElement>();
728
729           for(Language lang: viewProvider.getLanguages()) {
730             if (myProfile.isMyFile(file, lang, myOurPatternLanguage, myOurPatternLanguage2)) {
731               elementsToProcess.add(viewProvider.getPsi(lang));
732             }
733           }
734
735           return elementsToProcess;
736         }
737       });
738     }
739   }
740 }