update copyrights
[fedora-idea.git] / platform / lang-impl / src / com / intellij / codeInsight / folding / impl / DocumentFoldingInfo.java
bloba667dd956f435e95ff4f58da970bc50997087a5c
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.intellij.codeInsight.folding.impl;
19 import com.intellij.lang.ASTNode;
20 import com.intellij.lang.folding.FoldingBuilder;
21 import com.intellij.lang.folding.FoldingDescriptor;
22 import com.intellij.lang.folding.LanguageFolding;
23 import com.intellij.openapi.application.ApplicationManager;
24 import com.intellij.openapi.diagnostic.Logger;
25 import com.intellij.openapi.editor.Document;
26 import com.intellij.openapi.editor.Editor;
27 import com.intellij.openapi.editor.FoldRegion;
28 import com.intellij.openapi.editor.RangeMarker;
29 import com.intellij.openapi.editor.impl.FoldRegionImpl;
30 import com.intellij.openapi.fileEditor.FileDocumentManager;
31 import com.intellij.openapi.fileEditor.impl.text.CodeFoldingState;
32 import com.intellij.openapi.project.Project;
33 import com.intellij.openapi.util.JDOMExternalizable;
34 import com.intellij.openapi.util.TextRange;
35 import com.intellij.openapi.util.WriteExternalException;
36 import com.intellij.openapi.vfs.VirtualFile;
37 import com.intellij.psi.PsiDocumentManager;
38 import com.intellij.psi.PsiElement;
39 import com.intellij.psi.PsiFile;
40 import com.intellij.psi.PsiManager;
41 import com.intellij.util.text.StringTokenizer;
42 import org.jdom.Element;
43 import org.jetbrains.annotations.NonNls;
45 import java.util.*;
47 public class DocumentFoldingInfo implements JDOMExternalizable, CodeFoldingState {
48 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.folding.impl.DocumentFoldingInfo");
50 private final Project myProject;
51 private final VirtualFile myFile;
53 private final ArrayList<Object> myPsiElementsOrRangeMarkers = new ArrayList<Object>();
54 private final ArrayList<Boolean> myExpandedStates = new ArrayList<Boolean>();
55 private final Map<RangeMarker, String> myPlaceholderTexts = new HashMap<RangeMarker,String>();
56 private static final String DEFAULT_PLACEHOLDER = "...";
57 @NonNls private static final String ELEMENT_TAG = "element";
58 @NonNls private static final String SIGNATURE_ATT = "signature";
59 @NonNls private static final String EXPANDED_ATT = "expanded";
60 @NonNls private static final String MARKER_TAG = "marker";
61 @NonNls private static final String DATE_ATT = "date";
62 @NonNls private static final String PLACEHOLDER_ATT = "placeholder";
64 public DocumentFoldingInfo(Project project, Document document) {
65 myProject = project;
66 myFile = FileDocumentManager.getInstance().getFile(document);
69 public void loadFromEditor(Editor editor) {
70 clear();
72 PsiDocumentManager.getInstance(myProject).commitDocument(editor.getDocument());
74 EditorFoldingInfo info = EditorFoldingInfo.get(editor);
75 FoldRegion[] foldRegions = editor.getFoldingModel().getAllFoldRegions();
76 for (FoldRegion region : foldRegions) {
77 PsiElement element = info.getPsiElement(region);
78 boolean expanded = region.isExpanded();
79 boolean collapseByDefault = element != null &&
80 FoldingPolicy.isCollapseByDefault(element) &&
81 !FoldingUtil.caretInsideRange(editor, new TextRange(region.getStartOffset(), region.getEndOffset()));
82 if (collapseByDefault != !expanded || element == null) {
83 if (element != null) {
84 myPsiElementsOrRangeMarkers.add(element);
86 else if (region.isValid()) {
87 myPsiElementsOrRangeMarkers.add(region);
88 String placeholderText = region.getPlaceholderText();
89 myPlaceholderTexts.put(region, placeholderText);
91 myExpandedStates.add(expanded ? Boolean.TRUE : Boolean.FALSE);
96 void setToEditor(Editor editor) {
97 LOG.assertTrue(ApplicationManager.getApplication().isReadAccessAllowed());
98 final PsiManager psiManager = PsiManager.getInstance(myProject);
99 if (psiManager.isDisposed()) return;
101 if (!myFile.isValid()) return;
102 final PsiFile psiFile = psiManager.findFile(myFile);
103 if (psiFile == null) return;
105 Map<PsiElement, FoldingDescriptor> ranges = null;
106 for(int i = 0; i < myPsiElementsOrRangeMarkers.size(); i++){
107 Object o = myPsiElementsOrRangeMarkers.get(i);
108 if (o instanceof PsiElement) {
109 PsiElement element = (PsiElement)o;
110 if (!element.isValid()) continue;
112 if (ranges == null) ranges = buildRanges(editor, psiFile);
113 FoldingDescriptor descriptor = ranges.get(element);
114 if (descriptor == null) continue;
116 TextRange range = descriptor.getRange();
117 FoldRegion region = FoldingUtil.findFoldRegion(editor, range.getStartOffset(), range.getEndOffset());
118 if (region != null) {
119 boolean state = myExpandedStates.get(i).booleanValue();
120 region.setExpanded(state);
123 else if (o instanceof RangeMarker) {
124 RangeMarker marker = (RangeMarker)o;
125 if (!marker.isValid()) continue;
126 FoldRegion region = FoldingUtil.findFoldRegion(editor, marker.getStartOffset(), marker.getEndOffset());
127 if (region == null) {
128 String placeHolderText = myPlaceholderTexts.get(marker);
129 region = new FoldRegionImpl(editor, marker.getStartOffset(), marker.getEndOffset(), placeHolderText, null); //may fail to add in case intersecting region exists
130 if (!editor.getFoldingModel().addFoldRegion(region)) return;
133 boolean state = myExpandedStates.get(i).booleanValue();
134 region.setExpanded(state);
136 else{
137 LOG.error("o = " + o);
142 private static Map<PsiElement, FoldingDescriptor> buildRanges(final Editor editor, final PsiFile psiFile) {
143 final FoldingBuilder foldingBuilder = LanguageFolding.INSTANCE.forLanguage(psiFile.getLanguage());
144 final ASTNode node = psiFile.getNode();
145 if (node == null) return Collections.emptyMap();
146 final FoldingDescriptor[] descriptors = LanguageFolding.buildFoldingDescriptors(foldingBuilder, psiFile, editor.getDocument(), true);
147 Map<PsiElement, FoldingDescriptor> ranges = new HashMap<PsiElement, FoldingDescriptor>();
148 for (FoldingDescriptor descriptor : descriptors) {
149 final ASTNode ast = descriptor.getElement();
150 final PsiElement psi = ast.getPsi();
151 if (psi != null) {
152 ranges.put(psi, descriptor);
155 return ranges;
158 public void clear() {
159 myPsiElementsOrRangeMarkers.clear();
160 myExpandedStates.clear();
161 myPlaceholderTexts.clear();
164 public void writeExternal(Element element) throws WriteExternalException {
165 PsiDocumentManager.getInstance(myProject).commitAllDocuments();
167 if (myPsiElementsOrRangeMarkers.isEmpty()){
168 throw new WriteExternalException();
171 String date = null;
172 for(int i = 0; i < myPsiElementsOrRangeMarkers.size(); i++){
173 Object o = myPsiElementsOrRangeMarkers.get(i);
174 Boolean state = myExpandedStates.get(i);
175 if (o instanceof PsiElement){
176 PsiElement psiElement = (PsiElement)o;
177 if (!psiElement.isValid()) continue;
178 String signature = FoldingPolicy.getSignature(psiElement);
179 if (signature == null) continue;
181 PsiElement restoredElement = FoldingPolicy.restoreBySignature(psiElement.getContainingFile(), signature);
182 if (!psiElement.equals(restoredElement)){
183 restoredElement = FoldingPolicy.restoreBySignature(psiElement.getContainingFile(), signature);
184 LOG.assertTrue(false, "element:" + psiElement + ", signature:" + signature + ", file:" + psiElement.getContainingFile());
187 Element e = new Element(ELEMENT_TAG);
188 e.setAttribute(SIGNATURE_ATT, signature);
189 e.setAttribute(EXPANDED_ATT, state.toString());
190 element.addContent(e);
191 } else {
192 RangeMarker marker = (RangeMarker) o;
193 Element e = new Element(MARKER_TAG);
194 if (date == null) {
195 date = getTimeStamp();
197 if ("".equals(date)) continue;
199 e.setAttribute(DATE_ATT, date);
200 e.setAttribute(EXPANDED_ATT, state.toString());
201 String signature = Integer.valueOf(marker.getStartOffset()) + ":" + Integer.valueOf(marker.getEndOffset());
202 e.setAttribute(SIGNATURE_ATT, signature);
203 String placeHolderText = myPlaceholderTexts.get(marker);
204 e.setAttribute(PLACEHOLDER_ATT, placeHolderText);
205 element.addContent(e);
210 public void readExternal(Element element) {
211 myPsiElementsOrRangeMarkers.clear();
212 myExpandedStates.clear();
214 if (!myFile.isValid()) return;
216 final Document document = FileDocumentManager.getInstance().getDocument(myFile);
217 if (document == null) return;
219 PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(document);
220 if (psiFile == null || !psiFile.getViewProvider().isPhysical()) return;
222 String date = null;
223 for (final Object o : element.getChildren()) {
224 Element e = (Element)o;
225 if (ELEMENT_TAG.equals(e.getName())) {
226 String signature = e.getAttributeValue(SIGNATURE_ATT);
227 if (signature == null) {
228 continue;
230 PsiElement restoredElement = FoldingPolicy.restoreBySignature(psiFile, signature);
231 if (restoredElement != null) {
232 myPsiElementsOrRangeMarkers.add(restoredElement);
233 myExpandedStates.add(Boolean.valueOf(e.getAttributeValue(EXPANDED_ATT)));
236 else if (MARKER_TAG.equals(e.getName())) {
237 if (date == null) {
238 date = getTimeStamp();
240 if ("".equals(date)) continue;
242 if (!date.equals(e.getAttributeValue(DATE_ATT)) || FileDocumentManager.getInstance().isDocumentUnsaved(document)) continue;
243 StringTokenizer tokenizer = new StringTokenizer(e.getAttributeValue(SIGNATURE_ATT), ":");
244 try {
245 int start = Integer.valueOf(tokenizer.nextToken()).intValue();
246 int end = Integer.valueOf(tokenizer.nextToken()).intValue();
247 if (start < 0 || end >= document.getTextLength() || start > end) continue;
248 RangeMarker marker = document.createRangeMarker(start, end);
249 myPsiElementsOrRangeMarkers.add(marker);
250 myExpandedStates.add(Boolean.valueOf(e.getAttributeValue(EXPANDED_ATT)));
251 String placeHolderText = e.getAttributeValue(PLACEHOLDER_ATT);
252 if (placeHolderText == null) placeHolderText = DEFAULT_PLACEHOLDER;
253 myPlaceholderTexts.put(marker, placeHolderText);
255 catch (NoSuchElementException exc) {
256 LOG.error(exc);
259 else {
260 throw new IllegalStateException("unknown tag: " + e.getName());
265 private String getTimeStamp() {
266 if (!myFile.isValid()) return "";
267 return Long.toString(myFile.getTimeStamp());