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