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 @Expose private Map<Integer, AnswerPlaceholderSubtaskInfo> mySubtaskInfos = new HashMap<>();
37 public AnswerPlaceholder() {
40 public void initAnswerPlaceholder(final TaskFile file, boolean isRestarted) {
43 setInitialState(new MyInitialState(myOffset, length));
44 for (AnswerPlaceholderSubtaskInfo info : getSubtaskInfos().values()) {
45 info.setStatus(file.getTask().getStatus());
50 public int getIndex() {
54 public void setIndex(int index) {
59 * in actions {@link AnswerPlaceholder#getRealLength()} should be used
61 public int getLength() {
65 public void setLength(int length) {
70 public String getPossibleAnswer() {
71 return getActiveSubtaskInfo().getPossibleAnswer();
75 public void setPossibleAnswer(String possibleAnswer) {
76 getActiveSubtaskInfo().setPossibleAnswer(possibleAnswer);
79 public MyInitialState getInitialState() {
80 return myInitialState;
83 public void setInitialState(MyInitialState initialState) {
84 myInitialState = initialState;
88 public String getTaskText() {
89 return getActiveSubtaskInfo().getPlaceholderText();
93 public void setTaskText(String taskText) {
94 getActiveSubtaskInfo().setPlaceholderText(taskText);
98 public TaskFile getTaskFile() {
103 public void setTaskFile(TaskFile taskFile) {
104 myTaskFile = taskFile;
107 public int getPossibleAnswerLength() {
108 return getPossibleAnswer().length();
112 * Returns window to its initial state
114 public void reset() {
115 myOffset = myInitialState.getOffset();
116 length = myInitialState.getLength();
120 public StudyStatus getStatus() {
121 AnswerPlaceholderSubtaskInfo info = getActiveSubtaskInfo();
122 return info != null ? info.getStatus() : StudyStatus.Unchecked;
126 public void setStatus(StudyStatus status) {
127 getActiveSubtaskInfo().setStatus(status);
130 public boolean getSelected() {
134 public void setSelected(boolean selected) {
135 mySelected = selected;
139 setInitialState(new MyInitialState(myOffset, getTaskText().length()));
142 public boolean getUseLength() {
147 * @return length or possible answer length
149 public int getRealLength() {
150 return myUseLength ? getLength() : getVisibleLength(getActiveSubtaskIndex());
153 public void setUseLength(boolean useLength) {
154 myUseLength = useLength;
157 public int getOffset() {
161 public void setOffset(int offset) {
166 public List<String> getHints() {
167 return getActiveSubtaskInfo().getHints();
171 public void setHints(@NotNull final List<String> hints) {
172 getActiveSubtaskInfo().setHints(hints);
175 public void addHint(@NotNull final String text) {
176 getActiveSubtaskInfo().addHint(text);
179 public void removeHint(int i) {
180 getActiveSubtaskInfo().removeHint(i);
183 public Map<Integer, AnswerPlaceholderSubtaskInfo> getSubtaskInfos() {
184 return mySubtaskInfos;
187 public void setSubtaskInfos(Map<Integer, AnswerPlaceholderSubtaskInfo> subtaskInfos) {
188 mySubtaskInfos = subtaskInfos;
191 public boolean isActive() {
192 return getActiveSubtaskInfo() != null;
195 public static class MyInitialState {
196 private int length = -1;
197 private int offset = -1;
199 public MyInitialState() {
202 public MyInitialState(int initialOffset, int length) {
203 this.offset = initialOffset;
204 this.length = length;
207 public int getLength() {
211 public void setLength(int length) {
212 this.length = length;
215 public int getOffset() {
219 public void setOffset(int offset) {
220 this.offset = offset;
224 public AnswerPlaceholderSubtaskInfo getActiveSubtaskInfo() {
225 return mySubtaskInfos.get(getActiveSubtaskIndex());
228 public int getActiveSubtaskIndex() {
229 if (myTaskFile == null || myTaskFile.getTask() == null) {
232 return myTaskFile.getTask().getActiveSubtaskIndex();
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();
241 if (minIndex > subtaskIndex) {
242 return minInfo.isNeedInsertText() ? 0 : minInfo.getPlaceholderText().length();
244 int maxIndex = Collections.max(ContainerUtil.filter(mySubtaskInfos.keySet(), i -> i < subtaskIndex));
245 return getUseLength() ? length : mySubtaskInfos.get(maxIndex).getPossibleAnswer().length();
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)) {
253 String replacementText = mySubtaskInfos.get(toSubtask).getPossibleAnswer();
254 EduUtils.replaceAnswerPlaceholder(project, document, this, visibleLength, replacementText);
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);
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, "");
275 String replacementText = minInfo.getPlaceholderText();
276 EduUtils.replaceAnswerPlaceholder(project, document, this, visibleLength, replacementText);