update copyright
[fedora-idea.git] / xml / impl / src / com / intellij / lang / xml / XmlFormattingModel.java
blob522a64557197e73e3cf1a7dab50162ed46abd3bf
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.
18 * @author max
20 package com.intellij.lang.xml;
22 import com.intellij.formatting.Block;
23 import com.intellij.lang.ASTNode;
24 import com.intellij.openapi.diagnostic.Logger;
25 import com.intellij.openapi.project.Project;
26 import com.intellij.openapi.util.TextRange;
27 import com.intellij.psi.PsiFile;
28 import com.intellij.psi.PsiWhiteSpace;
29 import com.intellij.psi.TokenType;
30 import com.intellij.psi.formatter.FormatterUtil;
31 import com.intellij.psi.formatter.FormattingDocumentModelImpl;
32 import com.intellij.psi.formatter.PsiBasedFormattingModel;
33 import com.intellij.psi.impl.source.tree.TreeUtil;
34 import com.intellij.psi.tree.IElementType;
35 import com.intellij.psi.xml.XmlElementType;
36 import org.jetbrains.annotations.NonNls;
37 import org.jetbrains.annotations.Nullable;
39 public class XmlFormattingModel extends PsiBasedFormattingModel {
40 private static final Logger LOG =
41 Logger.getInstance("#com.intellij.psi.impl.source.codeStyle.PsiBasedFormatterModelWithShiftIndentInside");
43 private final Project myProject;
45 public XmlFormattingModel(final PsiFile file,
46 final Block rootBlock,
47 final FormattingDocumentModelImpl documentModel) {
48 super(file, rootBlock, documentModel);
49 myProject = file.getProject();
52 public TextRange shiftIndentInsideRange(TextRange textRange, int shift) {
53 return shiftIndentInsideWithPsi(textRange, shift);
56 public void commitChanges() {
60 private TextRange shiftIndentInsideWithPsi(final TextRange textRange, final int shift) {
61 final int offset = textRange.getStartOffset();
63 ASTNode leafElement = findElementAt(offset);
64 while (leafElement != null && !leafElement.getTextRange().equals(textRange)) {
65 leafElement = leafElement.getTreeParent();
68 return textRange;
71 protected String replaceWithPsiInLeaf(final TextRange textRange, String whiteSpace, ASTNode leafElement) {
72 if (!myCanModifyAllWhiteSpaces) {
73 if (leafElement.getElementType() == TokenType.WHITE_SPACE) return null;
74 LOG.assertTrue(leafElement.getPsi().isValid());
75 ASTNode prevNode = TreeUtil.prevLeaf(leafElement);
77 if (prevNode != null) {
78 IElementType type = prevNode.getElementType();
79 if(type == TokenType.WHITE_SPACE) {
80 final String text = prevNode.getText();
82 final @NonNls String cdataStartMarker = "<![CDATA[";
83 final int cdataPos = text.indexOf(cdataStartMarker);
84 if (cdataPos != -1 && whiteSpace.indexOf(cdataStartMarker) == -1) {
85 whiteSpace = mergeWsWithCdataMarker(whiteSpace, text, cdataPos);
86 if (whiteSpace == null) return null;
89 prevNode = TreeUtil.prevLeaf(prevNode);
90 type = prevNode != null ? prevNode.getElementType():null;
93 final @NonNls String cdataEndMarker = "]]>";
94 if(type == XmlElementType.XML_CDATA_END && whiteSpace.indexOf(cdataEndMarker) == -1) {
95 final ASTNode at = findElementAt(prevNode.getStartOffset());
97 if (at != null && at.getPsi() instanceof PsiWhiteSpace) {
98 final String s = at.getText();
99 final int cdataEndPos = s.indexOf(cdataEndMarker);
100 whiteSpace = mergeWsWithCdataMarker(whiteSpace, s, cdataEndPos);
101 leafElement = at;
102 } else {
103 whiteSpace = null;
105 if (whiteSpace == null) return null;
109 FormatterUtil.replaceWhiteSpace(whiteSpace, leafElement, TokenType.WHITE_SPACE, textRange);
110 return whiteSpace;
113 @Nullable
114 private static String mergeWsWithCdataMarker(String whiteSpace, final String s, final int cdataPos) {
115 final int firstCrInGeneratedWs = whiteSpace.indexOf('\n');
116 final int secondCrInGeneratedWs = firstCrInGeneratedWs != -1 ? whiteSpace.indexOf('\n', firstCrInGeneratedWs + 1):-1;
117 final int firstCrInPreviousWs = s.indexOf('\n');
118 final int secondCrInPreviousWs = firstCrInPreviousWs != -1 ? s.indexOf('\n', firstCrInPreviousWs + 1):-1;
120 boolean knowHowToModifyCData = false;
122 if (secondCrInPreviousWs != -1 && secondCrInGeneratedWs != -1 && cdataPos > firstCrInPreviousWs && cdataPos < secondCrInPreviousWs ) {
123 whiteSpace = whiteSpace.substring(0, secondCrInGeneratedWs) + s.substring(firstCrInPreviousWs + 1, secondCrInPreviousWs) + whiteSpace.substring(secondCrInGeneratedWs);
124 knowHowToModifyCData = true;
126 if (!knowHowToModifyCData) whiteSpace = null;
127 return whiteSpace;