fixed EDU-122 Exception while reloading empty task
authorLiana Bakradze <liana.bakradze@jetbrains.com>
Thu, 15 Jan 2015 14:47:17 +0000 (17:47 +0300)
committerLiana Bakradze <liana.bakradze@jetbrains.com>
Thu, 15 Jan 2015 14:47:17 +0000 (17:47 +0300)
in case of course deletion NPE happened and user got strange exception about listener also made a bit of code cleanup

python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyDocumentListener.java
python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyState.java
python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyUtils.java
python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java
python/educational/interactive-learning/src/com/jetbrains/edu/learning/course/Lesson.java
python/educational/interactive-learning/src/com/jetbrains/edu/learning/course/TaskFile.java
python/educational/interactive-learning/src/com/jetbrains/edu/learning/editor/StudyEditor.java

index 9714472a5649d1b329b6386dc5b044dba2b71f3f..a7a2e0debeaa111e79598f11978fcb04e3773392 100644 (file)
@@ -28,6 +28,9 @@ public class StudyDocumentListener extends DocumentAdapter {
   // with fragments containing "\n"
   @Override
   public void beforeDocumentChange(DocumentEvent e) {
+    if (!myTaskFile.isTrackChanges()) {
+      return;
+    }
     Document document = e.getDocument();
     myTaskWindows.clear();
     for (TaskWindow taskWindow : myTaskFile.getTaskWindows()) {
@@ -39,6 +42,9 @@ public class StudyDocumentListener extends DocumentAdapter {
 
   @Override
   public void documentChanged(DocumentEvent e) {
+    if (!myTaskFile.isTrackChanges()) {
+      return;
+    }
     if (e instanceof DocumentEventImpl) {
       DocumentEventImpl event = (DocumentEventImpl)e;
       Document document = e.getDocument();
index 4c382e0cc6221347f17cbbaf2ce8d055664c688c..1e890b0ebd306e9590292d308316c1852c44a24f 100644 (file)
@@ -6,6 +6,7 @@ import com.intellij.openapi.vfs.VirtualFile;
 import com.jetbrains.edu.learning.course.Task;
 import com.jetbrains.edu.learning.course.TaskFile;
 import com.jetbrains.edu.learning.editor.StudyEditor;
+import org.jetbrains.annotations.Nullable;
 
 public class StudyState {
   private final StudyEditor myStudyEditor;
@@ -15,7 +16,7 @@ public class StudyState {
   private final Task myTask;
   private final VirtualFile myTaskDir;
 
-  public StudyState(final StudyEditor studyEditor) {
+  public StudyState(@Nullable final StudyEditor studyEditor) {
     myStudyEditor = studyEditor;
     myEditor = studyEditor != null ? studyEditor.getEditor() : null;
     myTaskFile = studyEditor != null ? studyEditor.getTaskFile() : null;
index 9262ac6c7041f079a354422be2fd456f60d9532f..6f5f211f5ae1ada543c44fee0b303c97c4e0d073 100644 (file)
@@ -272,4 +272,10 @@ public class StudyUtils {
       extensions[0].setCommandLineParameters(cmd, project, filePath, pythonPath, currentTask);
     }
   }
+
+  public static void enableAction(@NotNull final AnActionEvent event, boolean isEnable) {
+    final Presentation presentation = event.getPresentation();
+    presentation.setVisible(isEnable);
+    presentation.setEnabled(isEnable);
+  }
 }
index 3458d252a15bca1e3642a9e9f810f8cdb85a59d2..c2ef5ab211a557a0db32993d1c4e5a17042781f5 100644 (file)
@@ -4,6 +4,7 @@ import com.intellij.ide.projectView.ProjectView;
 import com.intellij.openapi.actionSystem.AnActionEvent;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.command.CommandProcessor;
+import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.editor.Document;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.fileEditor.FileDocumentManager;
@@ -18,8 +19,7 @@ import com.intellij.openapi.util.io.FileUtil;
 import com.intellij.openapi.vfs.VfsUtil;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.openapi.wm.IdeFocusManager;
-import com.jetbrains.edu.learning.StudyDocumentListener;
-import com.jetbrains.edu.learning.StudyTaskManager;
+import com.jetbrains.edu.learning.StudyState;
 import com.jetbrains.edu.learning.StudyUtils;
 import com.jetbrains.edu.learning.course.*;
 import com.jetbrains.edu.learning.editor.StudyEditor;
@@ -30,8 +30,9 @@ import java.io.File;
 public class StudyRefreshTaskFileAction extends DumbAwareAction {
   public static final String ACTION_ID = "RefreshTaskAction";
   public static final String SHORTCUT = "ctrl shift pressed X";
+  private static final Logger LOG = Logger.getInstance(StudyRefreshTaskFileAction.class.getName());
 
-  public static void refresh(final Project project) {
+  public static void refresh(@NotNull final Project project) {
     ApplicationManager.getApplication().invokeLater(new Runnable() {
       @Override
       public void run() {
@@ -39,51 +40,54 @@ public class StudyRefreshTaskFileAction extends DumbAwareAction {
           @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
           @Override
           public void run() {
-            final Editor editor = StudyEditor.getSelectedEditor(project);
-            assert editor != null;
-            final Document document = editor.getDocument();
-            refreshFile(editor, document, project);
+            StudyEditor studyEditor = StudyEditor.getSelectedStudyEditor(project);
+            StudyState studyState = new StudyState(studyEditor);
+            if (studyEditor == null || !studyState.isValid()) {
+              LOG.info("RefreshTaskFileAction was invoked outside of Study Editor");
+              return;
+            }
+            refreshFile(studyState, project);
           }
         });
       }
     });
   }
 
-  public static void refreshFile(@NotNull final Editor editor, @NotNull final Document document, @NotNull final Project project) {
-    StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
-    Course course = taskManager.getCourse();
-    assert course != null;
-    FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
-    VirtualFile openedFile = fileDocumentManager.getFile(document);
-    assert openedFile != null;
-    final TaskFile selectedTaskFile = taskManager.getTaskFile(openedFile);
-    assert selectedTaskFile != null;
-    String openedFileName = openedFile.getName();
-    Task currentTask = selectedTaskFile.getTask();
-    resetTaskFile(document, project, course, selectedTaskFile, openedFileName, currentTask);
-    selectedTaskFile.drawAllWindows(editor);
-    selectedTaskFile.createGuardedBlocks(document, editor);
+  private static void refreshFile(@NotNull final StudyState studyState, @NotNull final Project project) {
+    final Editor editor = studyState.getEditor();
+    final TaskFile taskFile = studyState.getTaskFile();
+    if (!resetTaskFile(editor.getDocument(), project, taskFile, studyState.getVirtualFile().getName())) {
+      return;
+    }
+    taskFile.drawAllWindows(editor);
+    taskFile.createGuardedBlocks(editor);
     ApplicationManager.getApplication().invokeLater(new Runnable() {
       @Override
       public void run() {
         IdeFocusManager.getInstance(project).requestFocus(editor.getContentComponent(), true);
       }
     });
-    selectedTaskFile.navigateToFirstTaskWindow(editor);
-    showBalloon(project);
+    taskFile.navigateToFirstTaskWindow(editor);
+    showBalloon(project, "You can start again now", MessageType.INFO);
   }
 
-  public static void resetTaskFile(Document document, Project project, Course course, TaskFile taskFile, String name, Task task) {
-    resetDocument(document, course, name, task);
-    updateLessonInfo(task);
+  private static boolean resetTaskFile(@NotNull final Document document,
+                                       @NotNull final Project project,
+                                       TaskFile taskFile,
+                                       String name) {
+    if (!resetDocument(project, document, taskFile, name)) {
+      return false;
+    }
+    updateLessonInfo(taskFile.getTask());
     StudyUtils.updateStudyToolWindow(project);
     resetTaskWindows(taskFile);
     ProjectView.getInstance(project).refresh();
+    return true;
   }
 
-  private static void showBalloon(Project project) {
+  private static void showBalloon(@NotNull final Project project, String text, @NotNull final MessageType messageType) {
     BalloonBuilder balloonBuilder =
-      JBPopupFactory.getInstance().createHtmlTextBalloonBuilder("You can start again now", MessageType.INFO, null);
+      JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(text, messageType, null);
     final Balloon balloon = balloonBuilder.createBalloon();
     StudyEditor selectedStudyEditor = StudyEditor.getSelectedStudyEditor(project);
     assert selectedStudyEditor != null;
@@ -104,33 +108,38 @@ public class StudyRefreshTaskFileAction extends DumbAwareAction {
     lessonInfo.update(StudyStatus.Unchecked, +1);
   }
 
-  private static void resetDocument(final Document document, Course course, String fileName, Task task) {
+  private static boolean resetDocument(@NotNull final Project project,
+                                       @NotNull final Document document,
+                                       @NotNull final TaskFile taskFile,
+                                       String fileName) {
     StudyEditor.deleteGuardedBlocks(document);
-    StudyDocumentListener listener = StudyEditor.getListener(document);
-    if (listener != null) {
-      document.removeDocumentListener(listener);
-    }
+    taskFile.setTrackChanges(false);
     clearDocument(document);
+    Task task = taskFile.getTask();
     String lessonDir = Lesson.LESSON_DIR + String.valueOf(task.getLesson().getIndex() + 1);
     String taskDir = Task.TASK_DIR + String.valueOf(task.getIndex() + 1);
+    Course course = task.getLesson().getCourse();
     File resourceFile = new File(course.getResourcePath());
     File resourceRoot = resourceFile.getParentFile();
+    if (!resourceFile.exists() || resourceRoot == null) {
+      showBalloon(project, "Course was deleted", MessageType.ERROR);
+      return false;
+    }
     String patternPath = FileUtil.join(resourceRoot.getPath(), lessonDir, taskDir, fileName);
     VirtualFile patternFile = VfsUtil.findFileByIoFile(new File(patternPath), true);
     if (patternFile == null) {
-      return;
+      return false;
     }
-    Document patternDocument = FileDocumentManager.getInstance().getDocument(patternFile);
+    final Document patternDocument = FileDocumentManager.getInstance().getDocument(patternFile);
     if (patternDocument == null) {
-      return;
+      return false;
     }
     document.setText(patternDocument.getCharsSequence());
-    if (listener != null) {
-      document.addDocumentListener(listener);
-    }
+    taskFile.setTrackChanges(true);
+    return true;
   }
 
-  private static void clearDocument(final Document document) {
+  private static void clearDocument(@NotNull final Document document) {
     final int lineCount = document.getLineCount();
     if (lineCount != 0) {
       CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() {
@@ -142,7 +151,23 @@ public class StudyRefreshTaskFileAction extends DumbAwareAction {
     }
   }
 
-  public void actionPerformed(@NotNull AnActionEvent e) {
-    refresh(e.getProject());
+  public void actionPerformed(@NotNull AnActionEvent event) {
+    final Project project = event.getProject();
+    if (project != null) {
+      refresh(project);
+    }
+  }
+
+  @Override
+  public void update(AnActionEvent event) {
+    final Project project = event.getProject();
+    if (project != null) {
+      StudyEditor studyEditor = StudyEditor.getSelectedStudyEditor(project);
+      StudyState studyState = new StudyState(studyEditor);
+      if (studyState.isValid()) {
+        StudyUtils.enableAction(event, true);
+      }
+    }
+    StudyUtils.enableAction(event, false);
   }
 }
index 34be407bf5ef234e2158acd2b6a9504b2037b6fc..fef02eff0c60dc5d4c480159bd85477e0acc497f 100644 (file)
@@ -108,4 +108,8 @@ public class Lesson implements Stateful {
     }
     return myCourse.getLessons().get(myIndex - 1);
   }
+
+  public Course getCourse() {
+    return myCourse;
+  }
 }
index 1f5f7ebbbb52bfdfe7d9b97e97bdc5cb320cb5fc..5e835e076ac52aadcbfd9d3995f218c80f61a396 100644 (file)
@@ -36,6 +36,7 @@ public class TaskFile implements Stateful {
   private TaskWindow mySelectedTaskWindow = null;
   public int myIndex = -1;
   private boolean myUserCreated = false;
+  private boolean myTrackChanges = true;
 
   /**
    * @return if all the windows in task file are marked as resolved
@@ -107,7 +108,7 @@ public class TaskFile implements Stateful {
     final Document document = editor.getDocument();
     EditorActionManager.getInstance()
       .setReadonlyFragmentModificationHandler(document, new TaskWindowDeleteHandler(editor));
-    createGuardedBlocks(document, editor);
+    createGuardedBlocks(editor);
     editor.getColorsScheme().setColor(EditorColors.READONLY_FRAGMENT_BACKGROUND_COLOR, null);
   }
 
@@ -227,11 +228,15 @@ public class TaskFile implements Stateful {
   /**
    * Marks symbols adjacent to task windows as read-only fragments
    */
-  public void createGuardedBlocks(@NotNull final Document document, @NotNull final Editor editor) {
+  public void createGuardedBlocks(@NotNull final Editor editor) {
+    final Document document = editor.getDocument();
     if (document instanceof DocumentImpl) {
       DocumentImpl documentImpl = (DocumentImpl)document;
       List<RangeMarker> blocks = documentImpl.getGuardedBlocks();
       for (TaskWindow taskWindow : taskWindows) {
+        if (!taskWindow.isValid(document)) {
+          return;
+        }
         int start = taskWindow.getRealStartOffset(document);
         int end = start + taskWindow.getLength();
         if (start != 0) {
@@ -249,4 +254,12 @@ public class TaskFile implements Stateful {
       .addRangeHighlighter(start, end, HighlighterLayer.LAST + 1, null, HighlighterTargetArea.EXACT_RANGE);
     blocks.add(rh);
   }
+
+  public boolean isTrackChanges() {
+    return myTrackChanges;
+  }
+
+  public void setTrackChanges(boolean trackChanges) {
+    myTrackChanges = trackChanges;
+  }
 }
index a5c7edb9291af03b737214c4a90de676c8508109..19e0b47183c3d24a4f93ec435e173d687a5c846a 100644 (file)
@@ -59,7 +59,7 @@ import java.util.Map;
 
 /**
  * Implementation of StudyEditor which has panel with special buttons and task text
- * also @see {@link com.jetbrains.python.edu.editor.StudyFileEditorProvider}
+ * also @see {@link com.jetbrains.edu.learning.editor.StudyFileEditorProvider}
  */
 public class StudyEditor implements TextEditor {
   private static final String TASK_TEXT_HEADER = "Task Text";