package jetbrains.mps.idea.java.psiStubs; /*Generated by MPS */ import jetbrains.mps.extapi.persistence.DataSourceBase; import jetbrains.mps.idea.java.psi.JavaPsiListener; import com.intellij.openapi.module.Module; import com.intellij.psi.PsiDirectory; import java.util.List; import org.jetbrains.mps.openapi.persistence.DataSourceListener; import jetbrains.mps.internal.collections.runtime.ListSequence; import java.util.ArrayList; import java.util.Iterator; import jetbrains.mps.idea.java.psi.PsiChangesWatcher; import com.intellij.psi.PsiJavaFile; import jetbrains.mps.internal.collections.runtime.Sequence; import java.util.Collections; import java.util.Set; import jetbrains.mps.internal.collections.runtime.SetSequence; import java.util.HashSet; import com.intellij.psi.PsiFileSystemItem; public class PsiJavaStubDataSource extends DataSourceBase implements JavaFilesHolder, JavaPsiListener { private final Object LOCK = new Object(); private Module myModule; private PsiDirectory myDirectory; private List<DataSourceListener> myListeners = ListSequence.fromList(new ArrayList<DataSourceListener>()); public PsiJavaStubDataSource(Module module, PsiDirectory dir) { myModule = module; myDirectory = dir; } public void psiChanged(final JavaPsiListener.PsiEvent event) { // this is a guard against the situation when our directory has been removed // we don't notify our listeners about anything in this case // they should be removed anyways if (!(isValid())) { return; } // we've been told something has changed in PSI // let's see what matters to us PsiJavaStubEvent ourEvent = new PsiJavaStubDataSource.OurEvent(event); synchronized (LOCK) { { Iterator<DataSourceListener> listener_it = ListSequence.fromList(myListeners).iterator(); DataSourceListener listener_var; while (listener_it.hasNext()) { listener_var = listener_it.next(); if (listener_var instanceof PsiJavaStubListener) { ((PsiJavaStubListener) listener_var).changed(this, ourEvent); } else { listener_var.changed(this); } } } } } @Override public void addListener(DataSourceListener listener) { synchronized (LOCK) { if (ListSequence.fromList(myListeners).isEmpty()) { startListening(); } ListSequence.fromList(myListeners).addElement(listener); } } @Override public void removeListener(DataSourceListener listener) { synchronized (LOCK) { ListSequence.fromList(myListeners).removeElement(listener); if (ListSequence.fromList(myListeners).isEmpty()) { stopListening(); } } } /*package*/ void startListening() { myModule.getProject().getComponent(PsiChangesWatcher.class).addListener(this); } /*package*/ void stopListening() { myModule.getProject().getComponent(PsiChangesWatcher.class).removeListener(this); } public PsiDirectory getDirectory() { return myDirectory; } public Iterable<PsiJavaFile> getJavaFiles() { if (!(isValid())) { return Sequence.fromIterable(Collections.<PsiJavaFile>emptyList()); } return Sequence.fromIterable(Sequence.fromArray(myDirectory.getFiles())).ofType(PsiJavaFile.class); } private boolean isValid() { return myDirectory.isValid() && !(myModule.getProject().isDisposed()); } private class OurEvent extends PsiJavaStubEvent { private Set<PsiJavaFile> removed = SetSequence.fromSet(new HashSet<PsiJavaFile>()); private Set<JavaPsiListener.FSRename> renamed = SetSequence.fromSet(new HashSet<JavaPsiListener.FSRename>()); private Set<PsiJavaFile> reparse = SetSequence.fromSet(new HashSet<PsiJavaFile>()); /*package*/ OurEvent(JavaPsiListener.PsiEvent psiEvent) { // here we fill our data structures based on psi event // we do filtering based on whether items are related to this data source (i.e. this psiDirectory) for (PsiFileSystemItem fsItem : psiEvent.getRemoved()) { if (!(isOurJavaFile(fsItem))) { continue; } SetSequence.fromSet(removed).addElement(((PsiJavaFile) fsItem)); } for (JavaPsiListener.FSMove fsMove : psiEvent.getMoved()) { if (!(fsMove.item instanceof PsiJavaFile)) { continue; } if (myDirectory.equals(fsMove.to)) { SetSequence.fromSet(reparse).addElement(((PsiJavaFile) fsMove.item)); } else if (myDirectory.equals(fsMove.from)) { SetSequence.fromSet(removed).addElement(((PsiJavaFile) fsMove.item)); } } for (JavaPsiListener.FSRename fsRename : psiEvent.getRenamed()) { // FIXME the case when a java file was renamed to a non-java file is not handled // in this case we have to record the file as removed if (!(isOurJavaFile(fsRename.item))) { continue; } SetSequence.fromSet(renamed).addElement(fsRename); } for (PsiFileSystemItem fsItem : psiEvent.getChanged()) { if (!(isOurJavaFile(fsItem))) { continue; } SetSequence.fromSet(reparse).addElement(((PsiJavaFile) fsItem)); } for (PsiFileSystemItem fsItem : psiEvent.getCreated()) { if (!(isOurJavaFile(fsItem))) { continue; } SetSequence.fromSet(reparse).addElement(((PsiJavaFile) fsItem)); } } public Iterable<PsiJavaFile> removed() { return removed; } public Set<PsiJavaFile> needReparse() { return reparse; } public Set<JavaPsiListener.FSRename> renamed() { return renamed; } private boolean isOurJavaFile(PsiFileSystemItem fsItem) { if (!(fsItem instanceof PsiJavaFile)) { return false; } if (!(fsItem.isValid())) { return false; } if (!(myDirectory.equals(fsItem.getParent()))) { return false; } return true; } } }