fix serialization problem
[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   @Expose private Map<Integer, AnswerPlaceholderSubtaskInfo> mySubtaskInfos = new HashMap<>();
37   public AnswerPlaceholder() {
38   }
39
40   public void initAnswerPlaceholder(final TaskFile file, boolean isRestarted) {
41     setTaskFile(file);
42     if (!isRestarted) {
43       setInitialState(new MyInitialState(myOffset, length));
44       for (AnswerPlaceholderSubtaskInfo info : getSubtaskInfos().values()) {
45         info.setStatus(file.getTask().getStatus());
46       }
47     }
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   @Transient
120   public StudyStatus getStatus() {
121     AnswerPlaceholderSubtaskInfo info = getActiveSubtaskInfo();
122     return info != null ? info.getStatus() : StudyStatus.Unchecked;
123   }
124
125   @Transient
126   public void setStatus(StudyStatus status) {
127     getActiveSubtaskInfo().setStatus(status);
128   }
129
130   public boolean getSelected() {
131     return mySelected;
132   }
133
134   public void setSelected(boolean selected) {
135     mySelected = selected;
136   }
137
138   public void init() {
139     setInitialState(new MyInitialState(myOffset, getTaskText().length()));
140   }
141
142   public boolean getUseLength() {
143     return myUseLength;
144   }
145
146   /**
147    * @return length or possible answer length
148    */
149   public int getRealLength() {
150     return myUseLength ? getLength() : getVisibleLength(getActiveSubtaskIndex());
151   }
152
153   public void setUseLength(boolean useLength) {
154     myUseLength = useLength;
155   }
156
157   public int getOffset() {
158     return myOffset;
159   }
160
161   public void setOffset(int offset) {
162     myOffset = offset;
163   }
164
165   @Transient
166   public List<String> getHints() {
167     return getActiveSubtaskInfo().getHints();
168   }
169
170   @Transient
171   public void setHints(@NotNull final List<String> hints) {
172    getActiveSubtaskInfo().setHints(hints);
173   }
174
175   public void addHint(@NotNull final String text) {
176     getActiveSubtaskInfo().addHint(text);
177   }
178
179   public void removeHint(int i) {
180     getActiveSubtaskInfo().removeHint(i);
181   }
182
183   public Map<Integer, AnswerPlaceholderSubtaskInfo> getSubtaskInfos() {
184     return mySubtaskInfos;
185   }
186
187   public void setSubtaskInfos(Map<Integer, AnswerPlaceholderSubtaskInfo> subtaskInfos) {
188     mySubtaskInfos = subtaskInfos;
189   }
190
191   public boolean isActive() {
192     return getActiveSubtaskInfo() != null;
193   }
194
195   public static class MyInitialState {
196     private int length = -1;
197     private int offset = -1;
198
199     public MyInitialState() {
200     }
201
202     public MyInitialState(int initialOffset, int length) {
203       this.offset = initialOffset;
204       this.length = length;
205     }
206
207     public int getLength() {
208       return length;
209     }
210
211     public void setLength(int length) {
212       this.length = length;
213     }
214
215     public int getOffset() {
216       return offset;
217     }
218
219     public void setOffset(int offset) {
220       this.offset = offset;
221     }
222   }
223
224   public AnswerPlaceholderSubtaskInfo getActiveSubtaskInfo() {
225     return mySubtaskInfos.get(getActiveSubtaskIndex());
226   }
227
228   public int getActiveSubtaskIndex() {
229     if (myTaskFile == null || myTaskFile.getTask() == null) {
230       return 0;
231     }
232     return myTaskFile.getTask().getActiveSubtaskIndex();
233   }
234
235   public int getVisibleLength(int subtaskIndex) {
236     int minIndex = Collections.min(mySubtaskInfos.keySet());
237     AnswerPlaceholderSubtaskInfo minInfo = mySubtaskInfos.get(minIndex);
238     if (minIndex == subtaskIndex) {
239       return getUseLength() ? length : minInfo.getPossibleAnswer().length();
240     }
241     if (minIndex > subtaskIndex) {
242       return minInfo.isNeedInsertText() ? 0 : minInfo.getPlaceholderText().length();
243     }
244     int maxIndex = Collections.max(ContainerUtil.filter(mySubtaskInfos.keySet(), i -> i < subtaskIndex));
245     return getUseLength() ? length : mySubtaskInfos.get(maxIndex).getPossibleAnswer().length();
246   }
247
248   public void switchSubtask(@NotNull Project project, @NotNull Document document, int fromSubtask, int toSubtask) {
249     Set<Integer> indexes = mySubtaskInfos.keySet();
250     int visibleLength = getVisibleLength(fromSubtask);
251     if (indexes.contains(fromSubtask) && indexes.contains(toSubtask)) {
252       if (!myUseLength) {
253         String replacementText = mySubtaskInfos.get(toSubtask).getPossibleAnswer();
254         EduUtils.replaceAnswerPlaceholder(project, document, this, visibleLength, replacementText);
255       }
256       return;
257     }
258     Integer minIndex = Collections.min(indexes);
259     if (fromSubtask < toSubtask) {
260       if (minIndex > fromSubtask && minIndex <= toSubtask) {
261         Integer maxIndex = Collections.max(ContainerUtil.filter(indexes, integer -> integer <= toSubtask));
262         AnswerPlaceholderSubtaskInfo maxInfo = mySubtaskInfos.get(maxIndex);
263         String replacementText = myUseLength ? maxInfo.getPlaceholderText() : maxInfo.getPossibleAnswer();
264         EduUtils.replaceAnswerPlaceholder(project, document, this, visibleLength, replacementText);
265         return;
266       }
267     }
268     if (fromSubtask > toSubtask) {
269       if (minIndex > toSubtask && minIndex <= fromSubtask) {
270         AnswerPlaceholderSubtaskInfo minInfo = mySubtaskInfos.get(minIndex);
271         if (minInfo.isNeedInsertText()) {
272           EduUtils.replaceAnswerPlaceholder(project, document, this, visibleLength, "");
273         }
274         else {
275           String replacementText = minInfo.getPlaceholderText();
276           EduUtils.replaceAnswerPlaceholder(project, document, this, visibleLength, replacementText);
277         }
278       }
279     }
280   }
281 }