separate switch step and student file creation
[idea/community.git] / python / educational-core / student / src / com / jetbrains / edu / learning / courseFormat / AnswerPlaceholder.java
1 package com.jetbrains.edu.learning.courseFormat;
2
3 import com.google.gson.annotations.Expose;
4 import com.google.gson.annotations.SerializedName;
5 import com.intellij.openapi.editor.Document;
6 import com.intellij.openapi.project.Project;
7 import com.intellij.util.containers.ContainerUtil;
8 import com.intellij.util.containers.hash.HashMap;
9 import com.intellij.util.xmlb.annotations.Transient;
10 import com.jetbrains.edu.learning.core.EduUtils;
11 import org.jetbrains.annotations.NotNull;
12
13 import java.util.Collections;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Set;
17
18 /**
19  * Implementation of windows which user should type in
20  */
21
22 public class AnswerPlaceholder {
23
24   @SerializedName("offset")
25   @Expose private int myOffset = -1;
26
27   @Expose private int length = -1;
28
29   private int myIndex = -1;
30   private MyInitialState myInitialState;
31   private StudyStatus myStatus = StudyStatus.Unchecked;
32   private boolean mySelected = false;
33   private boolean myUseLength = true;
34
35   @Transient private TaskFile myTaskFile;
36
37   @Expose private Map<Integer, AnswerPlaceholderSubtaskInfo> mySubtaskInfos = new HashMap<>();
38   public AnswerPlaceholder() {
39   }
40
41   public void initAnswerPlaceholder(final TaskFile file, boolean isRestarted) {
42     if (!isRestarted) {
43       setInitialState(new MyInitialState(myOffset, length));
44       myStatus = file.getTask().getStatus();
45     }
46
47     setTaskFile(file);
48   }
49
50   public int getIndex() {
51     return myIndex;
52   }
53
54   public void setIndex(int index) {
55     myIndex = index;
56   }
57
58   /**
59    * in actions {@link AnswerPlaceholder#getRealLength()} should be used
60    */
61   public int getLength() {
62     return length;
63   }
64
65   public void setLength(int length) {
66     this.length = length;
67   }
68
69   @Transient
70   public String getPossibleAnswer() {
71     return getActiveSubtaskInfo().getPossibleAnswer();
72   }
73
74   @Transient
75   public void setPossibleAnswer(String possibleAnswer) {
76     getActiveSubtaskInfo().setPossibleAnswer(possibleAnswer);
77   }
78
79   public MyInitialState getInitialState() {
80     return myInitialState;
81   }
82
83   public void setInitialState(MyInitialState initialState) {
84     myInitialState = initialState;
85   }
86
87   @Transient
88   public String getTaskText() {
89     return getActiveSubtaskInfo().getPlaceholderText();
90   }
91
92   @Transient
93   public void setTaskText(String taskText) {
94     getActiveSubtaskInfo().setPlaceholderText(taskText);
95   }
96
97   @Transient
98   public TaskFile getTaskFile() {
99     return myTaskFile;
100   }
101
102   @Transient
103   public void setTaskFile(TaskFile taskFile) {
104     myTaskFile = taskFile;
105   }
106
107   public int getPossibleAnswerLength() {
108     return getPossibleAnswer().length();
109   }
110
111   /**
112    * Returns window to its initial state
113    */
114   public void reset() {
115     myOffset = myInitialState.getOffset();
116     length = myInitialState.getLength();
117   }
118
119   public StudyStatus getStatus() {
120     return myStatus;
121   }
122
123   public void setStatus(StudyStatus status) {
124     myStatus = status;
125   }
126
127   public boolean getSelected() {
128     return mySelected;
129   }
130
131   public void setSelected(boolean selected) {
132     mySelected = selected;
133   }
134
135   public void init() {
136     setInitialState(new MyInitialState(myOffset, getTaskText().length()));
137   }
138
139   public boolean getUseLength() {
140     return myUseLength;
141   }
142
143   /**
144    * @return length or possible answer length
145    */
146   public int getRealLength() {
147     return myUseLength ? getLength() : getVisibleLength(getActiveSubtaskIndex());
148   }
149
150   public void setUseLength(boolean useLength) {
151     myUseLength = useLength;
152   }
153
154   public int getOffset() {
155     return myOffset;
156   }
157
158   public void setOffset(int offset) {
159     myOffset = offset;
160   }
161
162   @Transient
163   public List<String> getHints() {
164     return getActiveSubtaskInfo().getHints();
165   }
166
167   @Transient
168   public void setHints(@NotNull final List<String> hints) {
169    getActiveSubtaskInfo().setHints(hints);
170   }
171
172   public void addHint(@NotNull final String text) {
173     getActiveSubtaskInfo().addHint(text);
174   }
175
176   public void removeHint(int i) {
177     getActiveSubtaskInfo().removeHint(i);
178   }
179
180   public Map<Integer, AnswerPlaceholderSubtaskInfo> getSubtaskInfos() {
181     return mySubtaskInfos;
182   }
183
184   public void setSubtaskInfos(Map<Integer, AnswerPlaceholderSubtaskInfo> subtaskInfos) {
185     mySubtaskInfos = subtaskInfos;
186   }
187
188   public boolean isActive() {
189     return getActiveSubtaskInfo() != null;
190   }
191
192   public static class MyInitialState {
193     private int length = -1;
194     private int offset = -1;
195
196     public MyInitialState() {
197     }
198
199     public MyInitialState(int initialOffset, int length) {
200       this.offset = initialOffset;
201       this.length = length;
202     }
203
204     public int getLength() {
205       return length;
206     }
207
208     public void setLength(int length) {
209       this.length = length;
210     }
211
212     public int getOffset() {
213       return offset;
214     }
215
216     public void setOffset(int offset) {
217       this.offset = offset;
218     }
219   }
220
221   public AnswerPlaceholderSubtaskInfo getActiveSubtaskInfo() {
222     return mySubtaskInfos.get(getActiveSubtaskIndex());
223   }
224
225   public int getActiveSubtaskIndex() {
226     if (myTaskFile == null || myTaskFile.getTask() == null) {
227       return 0;
228     }
229     return myTaskFile.getTask().getActiveSubtaskIndex();
230   }
231
232   public int getVisibleLength(int subtaskIndex) {
233     int minIndex = Collections.min(mySubtaskInfos.keySet());
234     AnswerPlaceholderSubtaskInfo minInfo = mySubtaskInfos.get(minIndex);
235     if (minIndex == subtaskIndex) {
236       return getUseLength() ? length : minInfo.getPossibleAnswer().length();
237     }
238     if (minIndex > subtaskIndex) {
239       return minInfo.isNeedInsertText() ? 0 : minInfo.getPlaceholderText().length();
240     }
241     int maxIndex = Collections.max(ContainerUtil.filter(mySubtaskInfos.keySet(), i -> i < subtaskIndex));
242     return getUseLength() ? length : mySubtaskInfos.get(maxIndex).getPossibleAnswer().length();
243   }
244
245   public void switchSubtask(@NotNull Project project, @NotNull Document document, int fromSubtask, int toSubtask) {
246     Set<Integer> indexes = mySubtaskInfos.keySet();
247     int visibleLength = getVisibleLength(fromSubtask);
248     if (indexes.contains(fromSubtask) && indexes.contains(toSubtask)) {
249       if (!myUseLength) {
250         String replacementText = mySubtaskInfos.get(toSubtask).getPossibleAnswer();
251         EduUtils.replaceAnswerPlaceholder(project, document, this, visibleLength, replacementText);
252       }
253       return;
254     }
255     Integer minIndex = Collections.min(indexes);
256     if (fromSubtask < toSubtask) {
257       if (minIndex > fromSubtask && minIndex <= toSubtask) {
258         Integer maxIndex = Collections.max(ContainerUtil.filter(indexes, integer -> integer <= toSubtask));
259         AnswerPlaceholderSubtaskInfo maxInfo = mySubtaskInfos.get(maxIndex);
260         String replacementText = myUseLength ? maxInfo.getPlaceholderText() : maxInfo.getPossibleAnswer();
261         EduUtils.replaceAnswerPlaceholder(project, document, this, visibleLength, replacementText);
262         return;
263       }
264     }
265     if (fromSubtask > toSubtask) {
266       if (minIndex > toSubtask && minIndex <= fromSubtask) {
267         AnswerPlaceholderSubtaskInfo minInfo = mySubtaskInfos.get(minIndex);
268         if (minInfo.isNeedInsertText()) {
269           EduUtils.replaceAnswerPlaceholder(project, document, this, visibleLength, "");
270         }
271         else {
272           String replacementText = minInfo.getPlaceholderText();
273           EduUtils.replaceAnswerPlaceholder(project, document, this, visibleLength, replacementText);
274         }
275       }
276     }
277   }
278 }