summaryrefslogtreecommitdiff
path: root/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskFile.java
blob: 4f17fc0d27f3ad095f864d6fc0ab6ef657d3f97f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
package com.jetbrains.python.edu.course;

import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.xmlb.annotations.Transient;
import com.jetbrains.python.edu.StudyUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Implementation of task file which contains task windows for student to type in and
 * which is visible to student in project view
 */

public class TaskFile implements Stateful{
  public List<TaskWindow> taskWindows = new ArrayList<TaskWindow>();
  private Task myTask;
  @Transient
  private TaskWindow mySelectedTaskWindow = null;
  public int myIndex = -1;
  private boolean myUserCreated = false;

  /**
   * @return if all the windows in task file are marked as resolved
   */
  @Transient
  public StudyStatus getStatus() {
    for (TaskWindow taskWindow : taskWindows) {
      StudyStatus windowStatus = taskWindow.getStatus();
      if (windowStatus == StudyStatus.Failed) {
        return StudyStatus.Failed;
      }
      if (windowStatus == StudyStatus.Unchecked) {
        return StudyStatus.Unchecked;
      }
    }
    return StudyStatus.Solved;
  }

  public Task getTask() {
    return myTask;
  }

  @Nullable
  @Transient
  public TaskWindow getSelectedTaskWindow() {
    return mySelectedTaskWindow;
  }

  /**
   * @param selectedTaskWindow window from this task file to be set as selected
   */
  public void setSelectedTaskWindow(@NotNull final TaskWindow selectedTaskWindow) {
    if (selectedTaskWindow.getTaskFile() == this) {
      mySelectedTaskWindow = selectedTaskWindow;
    }
    else {
      throw new IllegalArgumentException("Window may be set as selected only in task file which it belongs to");
    }
  }

  public List<TaskWindow> getTaskWindows() {
    return taskWindows;
  }

  /**
   * Creates task files in its task folder in project user created
   *
   * @param taskDir      project directory of task which task file belongs to
   * @param resourceRoot directory where original task file stored
   * @throws java.io.IOException
   */
  public void create(@NotNull final VirtualFile taskDir, @NotNull final File resourceRoot,
                     @NotNull final String name) throws IOException {
    String systemIndependentName = FileUtil.toSystemIndependentName(name);
    final int index = systemIndependentName.lastIndexOf("/");
    if (index > 0) {
      systemIndependentName = systemIndependentName.substring(index + 1);
    }
    File resourceFile = new File(resourceRoot, name);
    File fileInProject = new File(taskDir.getPath(), systemIndependentName);
    FileUtil.copy(resourceFile, fileInProject);
  }

  public void drawAllWindows(Editor editor) {
    for (TaskWindow taskWindow : taskWindows) {
      taskWindow.draw(editor, false, false);
    }
  }


  /**
   * @param pos position in editor
   * @return task window located in specified position or null if there is no task window in this position
   */
  @Nullable
  public TaskWindow getTaskWindow(@NotNull final Document document, @NotNull final LogicalPosition pos) {
    int line = pos.line;
    if (line >= document.getLineCount()) {
      return null;
    }
    int column = pos.column;
    int offset = document.getLineStartOffset(line) + column;
    for (TaskWindow tw : taskWindows) {
      if (tw.getLine() <= line) {
        int twStartOffset = tw.getRealStartOffset(document);
        final int length = tw.getLength() > 0 ? tw.getLength() : 0;
        int twEndOffset = twStartOffset + length;
        if (twStartOffset <= offset && offset <= twEndOffset) {
          return tw;
        }
      }
    }
    return null;
  }

  /**
   * Updates task window lines
   *
   * @param startLine lines greater than this line and including this line will be updated
   * @param change    change to be added to line numbers
   */
  public void incrementLines(int startLine, int change) {
    for (TaskWindow taskTaskWindow : taskWindows) {
      if (taskTaskWindow.getLine() >= startLine) {
        taskTaskWindow.setLine(taskTaskWindow.getLine() + change);
      }
    }
  }

  /**
   * Initializes state of task file
   *
   * @param task task which task file belongs to
   */

  public void init(final Task task, boolean isRestarted) {
    myTask = task;
    for (TaskWindow taskWindow : taskWindows) {
      taskWindow.init(this, isRestarted);
    }
    Collections.sort(taskWindows);
    for (int i = 0; i < taskWindows.size(); i++) {
      taskWindows.get(i).setIndex(i);
    }
  }

  /**
   * @param index index of task file in list of task files of its task
   */
  public void setIndex(int index) {
    myIndex = index;
  }

  /**
   * Updates windows in specific line
   *
   * @param lineChange         change in line number
   * @param line               line to be updated
   * @param newEndOffsetInLine distance from line start to end of inserted fragment
   * @param oldEndOffsetInLine distance from line start to end of changed fragment
   */
  public void updateLine(int lineChange, int line, int newEndOffsetInLine, int oldEndOffsetInLine) {
    for (TaskWindow w : taskWindows) {
      if ((w.getLine() == line) && (w.getStart() >= oldEndOffsetInLine)) {
        int distance = w.getStart() - oldEndOffsetInLine;
        if (lineChange != 0 || newEndOffsetInLine <= w.getStart()) {
          w.setStart(distance + newEndOffsetInLine);
          w.setLine(line + lineChange);
        }
      }
    }
  }

  public static void copy(@NotNull final TaskFile source, @NotNull final TaskFile target) {
    List<TaskWindow> sourceTaskWindows = source.getTaskWindows();
    List<TaskWindow> windowsCopy = new ArrayList<TaskWindow>(sourceTaskWindows.size());
    for (TaskWindow taskWindow : sourceTaskWindows) {
      TaskWindow taskWindowCopy = new TaskWindow();
      taskWindowCopy.setLine(taskWindow.getLine());
      taskWindowCopy.setStart(taskWindow.getStart());
      taskWindowCopy.setLength(taskWindow.getLength());
      taskWindowCopy.setPossibleAnswer(taskWindow.getPossibleAnswer());
      taskWindowCopy.setIndex(taskWindow.getIndex());
      windowsCopy.add(taskWindowCopy);
    }
    target.setTaskWindows(windowsCopy);
  }

  public void setTaskWindows(List<TaskWindow> taskWindows) {
    this.taskWindows = taskWindows;
  }

  public void setStatus(@NotNull final StudyStatus status, @NotNull final StudyStatus oldStatus) {
    for (TaskWindow taskWindow : taskWindows) {
      taskWindow.setStatus(status, oldStatus);
    }
  }

  public void setUserCreated(boolean userCreated) {
    myUserCreated = userCreated;
  }

  public boolean isUserCreated() {
    return myUserCreated;
  }

  public void navigateToFirstTaskWindow(@NotNull final Editor editor) {
    if (!taskWindows.isEmpty()) {
      TaskWindow firstTaskWindow = StudyUtils.getFirst(taskWindows);
      mySelectedTaskWindow = firstTaskWindow;
      LogicalPosition taskWindowStart = new LogicalPosition(firstTaskWindow.getLine(), firstTaskWindow.getStart());
      editor.getCaretModel().moveToLogicalPosition(taskWindowStart);
      int startOffset = firstTaskWindow.getRealStartOffset(editor.getDocument());
      int endOffset = startOffset + firstTaskWindow.getLength();
      editor.getSelectionModel().setSelection(startOffset, endOffset);
    }
  }
}