1 package com.jetbrains.edu.learning.courseFormat;
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;
13 import java.util.Collections;
14 import java.util.List;
19 * Implementation of windows which user should type in
22 public class AnswerPlaceholder {
24 @SerializedName("offset")
25 @Expose private int myOffset = -1;
27 @Expose private int length = -1;
29 private int myIndex = -1;
30 private MyInitialState myInitialState;
31 private boolean mySelected = false;
32 private boolean myUseLength = true;
34 @Transient private TaskFile myTaskFile;
36 @SerializedName("subtask_infos")
37 @Expose private Map<Integer, AnswerPlaceholderSubtaskInfo> mySubtaskInfos = new HashMap<>();
38 public AnswerPlaceholder() {
41 public void initAnswerPlaceholder(final TaskFile file, boolean isRestarted) {
44 setInitialState(new MyInitialState(myOffset, length));
45 for (AnswerPlaceholderSubtaskInfo info : getSubtaskInfos().values()) {
46 info.setStatus(file.getTask().getStatus());
51 public int getIndex() {
55 public void setIndex(int index) {
60 * in actions {@link AnswerPlaceholder#getRealLength()} should be used
62 public int getLength() {
66 public void setLength(int length) {
71 public String getPossibleAnswer() {
72 return getActiveSubtaskInfo().getPossibleAnswer();
76 public void setPossibleAnswer(String possibleAnswer) {
77 getActiveSubtaskInfo().setPossibleAnswer(possibleAnswer);
80 public MyInitialState getInitialState() {
81 return myInitialState;
84 public void setInitialState(MyInitialState initialState) {
85 myInitialState = initialState;
89 public String getTaskText() {
90 return getActiveSubtaskInfo().getPlaceholderText();
94 public void setTaskText(String taskText) {
95 getActiveSubtaskInfo().setPlaceholderText(taskText);
99 public TaskFile getTaskFile() {
104 public void setTaskFile(TaskFile taskFile) {
105 myTaskFile = taskFile;
108 public int getPossibleAnswerLength() {
109 return getPossibleAnswer().length();
113 * Returns window to its initial state
115 public void reset() {
116 myOffset = myInitialState.getOffset();
117 length = myInitialState.getLength();
121 public StudyStatus getStatus() {
122 AnswerPlaceholderSubtaskInfo info = getActiveSubtaskInfo();
123 return info != null ? info.getStatus() : StudyStatus.Unchecked;
127 public void setStatus(StudyStatus status) {
128 getActiveSubtaskInfo().setStatus(status);
131 public boolean getSelected() {
135 public void setSelected(boolean selected) {
136 mySelected = selected;
140 setInitialState(new MyInitialState(myOffset, getTaskText().length()));
143 public boolean getUseLength() {
148 * @return length or possible answer length
150 public int getRealLength() {
151 return myUseLength ? getLength() : getVisibleLength(getActiveSubtaskIndex());
154 public void setUseLength(boolean useLength) {
155 myUseLength = useLength;
158 public int getOffset() {
162 public void setOffset(int offset) {
167 public List<String> getHints() {
168 return getActiveSubtaskInfo().getHints();
172 public void setHints(@NotNull final List<String> hints) {
173 getActiveSubtaskInfo().setHints(hints);
176 public void addHint(@NotNull final String text) {
177 getActiveSubtaskInfo().addHint(text);
180 public void removeHint(int i) {
181 getActiveSubtaskInfo().removeHint(i);
184 public Map<Integer, AnswerPlaceholderSubtaskInfo> getSubtaskInfos() {
185 return mySubtaskInfos;
188 public void setSubtaskInfos(Map<Integer, AnswerPlaceholderSubtaskInfo> subtaskInfos) {
189 mySubtaskInfos = subtaskInfos;
192 public boolean isActive() {
193 return getActiveSubtaskInfo() != null;
196 public static class MyInitialState {
197 private int length = -1;
198 private int offset = -1;
200 public MyInitialState() {
203 public MyInitialState(int initialOffset, int length) {
204 this.offset = initialOffset;
205 this.length = length;
208 public int getLength() {
212 public void setLength(int length) {
213 this.length = length;
216 public int getOffset() {
220 public void setOffset(int offset) {
221 this.offset = offset;
225 public AnswerPlaceholderSubtaskInfo getActiveSubtaskInfo() {
226 return mySubtaskInfos.get(getActiveSubtaskIndex());
229 public int getActiveSubtaskIndex() {
230 if (myTaskFile == null || myTaskFile.getTask() == null) {
233 return myTaskFile.getTask().getActiveSubtaskIndex();
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();
242 if (minIndex > subtaskIndex) {
243 return minInfo.isNeedInsertText() ? 0 : minInfo.getPlaceholderText().length();
245 int maxIndex = Collections.max(ContainerUtil.filter(mySubtaskInfos.keySet(), i -> i <= subtaskIndex));
246 return getUseLength() ? length : mySubtaskInfos.get(maxIndex).getPossibleAnswer().length();
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)) {
254 String replacementText = mySubtaskInfos.get(toSubtask).getPossibleAnswer();
255 EduUtils.replaceAnswerPlaceholder(project, document, this, visibleLength, replacementText);
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);
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, "");
276 String replacementText = minInfo.getPlaceholderText();
277 EduUtils.replaceAnswerPlaceholder(project, document, this, visibleLength, replacementText);