update copyright
[fedora-idea.git] / xml / impl / src / com / intellij / psi / formatter / xml / HtmlPolicy.java
blobf3e35acaff1dec5470568e7425cfb16cbf59865b
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.
16 package com.intellij.psi.formatter.xml;
18 import com.intellij.formatting.FormattingDocumentModel;
19 import com.intellij.formatting.WrapType;
20 import com.intellij.lang.ASTNode;
21 import com.intellij.openapi.util.TextRange;
22 import com.intellij.psi.PsiElement;
23 import com.intellij.psi.TokenType;
24 import com.intellij.psi.codeStyle.CodeStyleSettings;
25 import com.intellij.psi.impl.source.SourceTreeToPsiMap;
26 import com.intellij.psi.impl.source.tree.LeafElement;
27 import com.intellij.psi.xml.XmlElementType;
28 import com.intellij.psi.xml.XmlTag;
30 import java.util.HashMap;
31 import java.util.Map;
33 public class HtmlPolicy extends XmlFormattingPolicy {
35 protected final CodeStyleSettings mySettings;
37 public HtmlPolicy(final CodeStyleSettings settings, final FormattingDocumentModel documentModel) {
38 super(documentModel);
39 mySettings = settings;
43 public boolean indentChildrenOf(final XmlTag parentTag) {
44 if (parentTag == null) {
45 return true;
47 final PsiElement firstChild = findFirstNonEmptyChild(parentTag);
49 if (firstChild == null) {
50 return false;
53 if (firstChild.getNode().getElementType() != XmlElementType.XML_START_TAG_START) {
54 return false;
57 if (mySettings.HTML_DO_NOT_ALIGN_CHILDREN_OF_MIN_LINES > 0 && getLines(parentTag) > mySettings.HTML_DO_NOT_ALIGN_CHILDREN_OF_MIN_LINES)
59 return false;
61 else {
62 return !checkName(parentTag, mySettings.HTML_DO_NOT_INDENT_CHILDREN_OF);
66 private PsiElement findFirstNonEmptyChild(final XmlTag parentTag) {
67 PsiElement result = parentTag.getFirstChild();
68 while (result != null && result.getTextLength() == 0) {
69 result = result.getNextSibling();
71 return result;
74 private int getLines(final XmlTag parentTag) {
75 final TextRange textRange = parentTag.getTextRange();
76 return myDocumentModel.getLineNumber(textRange.getEndOffset()) - myDocumentModel.getLineNumber(textRange.getStartOffset());
79 public boolean insertLineBreakBeforeTag(final XmlTag xmlTag) {
80 PsiElement prev = xmlTag.getPrevSibling();
81 if (prev == null) return false;
82 ASTNode prevNode = SourceTreeToPsiMap.psiElementToTree(prev);
83 while (prevNode != null && containsWhiteSpacesOnly(prevNode)) {
84 prevNode = prevNode.getTreePrev();
86 if (prevNode == null) return false;
87 if (!(SourceTreeToPsiMap.treeElementToPsi(prevNode)instanceof XmlTag)) return false;
88 return checkName(xmlTag, mySettings.HTML_ELEMENTS_TO_INSERT_NEW_LINE_BEFORE);
91 private boolean containsWhiteSpacesOnly(final ASTNode node) {
92 if (node == null) return false;
93 if (node.getElementType() == TokenType.WHITE_SPACE) return true;
94 if (node instanceof LeafElement) return false;
95 ASTNode child = node.getFirstChildNode();
96 while (child != null) {
97 if (!containsWhiteSpacesOnly(child)) return false;
98 child = child.getTreeNext();
100 return true;
103 public boolean removeLineBreakBeforeTag(final XmlTag xmlTag) {
104 return checkName(xmlTag, mySettings.HTML_ELEMENTS_TO_REMOVE_NEW_LINE_BEFORE);
107 protected boolean checkName(XmlTag tag, String option) {
108 if (option == null) return false;
109 for (String name : getTagNames(option)) {
110 if (name.trim().equalsIgnoreCase(tag.getName())) return true;
112 return false;
115 private final Map<String, String[]> myCachedSplits = new HashMap<String, String[]>();
117 private String[] getTagNames(final String option) {
118 String[] splits = myCachedSplits.get(option);
119 if (splits == null) {
120 splits = option.split(",");
121 myCachedSplits.put(option, splits);
123 return splits;
126 public boolean keepWhiteSpacesInsideTag(final XmlTag tag) {
127 return checkName(tag, mySettings.HTML_KEEP_WHITESPACES_INSIDE) || "jsp:attribute".equals(tag.getName());
130 public WrapType getWrappingTypeForTagEnd(final XmlTag xmlTag) {
131 return shouldBeWrapped(xmlTag) ? WrapType.ALWAYS : WrapType.NORMAL;
134 public WrapType getWrappingTypeForTagBegin(final XmlTag tag) {
135 if (shouldBeWrapped(tag)) {
136 return WrapType.ALWAYS;
139 if (!isInlineTag(tag)) {
141 if (checkName(tag, mySettings.HTML_DONT_ADD_BREAKS_IF_INLINE_CONTENT)) {
142 if (hasInlineContentOnly(tag)) return WrapType.NORMAL;
145 return WrapType.ALWAYS;
148 return WrapType.NORMAL;
151 private boolean hasInlineContentOnly(final XmlTag tag) {
152 final XmlTag[] tags = tag.getSubTags();
153 for (int i = 0; i < tags.length; i++) {
154 XmlTag xmlTag = tags[i];
155 if (!isInlineTag(xmlTag)) return false;
156 if (!hasInlineContentOnly(xmlTag)) return false;
159 return true;
162 protected boolean isInlineTag(final XmlTag tag) {
163 return checkName(tag, mySettings.HTML_INLINE_ELEMENTS);
166 protected boolean shouldBeWrapped(final XmlTag tag) {
167 return false;
170 public boolean isTextElement(XmlTag tag) {
171 return isInlineTag(tag);
174 public int getTextWrap(final XmlTag tag) {
175 return mySettings.HTML_TEXT_WRAP;
178 public int getAttributesWrap() {
179 return mySettings.HTML_ATTRIBUTE_WRAP;
182 public boolean getShouldAlignAttributes() {
183 return mySettings.HTML_ALIGN_ATTRIBUTES;
186 public boolean getShouldAlignText() {
187 return mySettings.HTML_ALIGN_TEXT;
190 public boolean getShouldKeepWhiteSpaces() {
191 return mySettings.HTML_KEEP_WHITESPACES;
194 public boolean getShouldAddSpaceAroundEqualityInAttribute() {
195 return mySettings.HTML_SPACE_AROUND_EQUALITY_IN_ATTRINUTE;
198 public boolean getShouldAddSpaceAroundTagName() {
199 return mySettings.HTML_SPACE_AFTER_TAG_NAME;
202 public int getKeepBlankLines() {
203 return mySettings.HTML_KEEP_BLANK_LINES;
206 public boolean getShouldKeepLineBreaks() {
207 return mySettings.HTML_KEEP_LINE_BREAKS;
210 public boolean getShouldKeepLineBreaksInText() {
211 return mySettings.HTML_KEEP_LINE_BREAKS_IN_TEXT;
214 public CodeStyleSettings getSettings() {
215 return mySettings;
218 public boolean addSpaceIntoEmptyTag() {
219 return mySettings.HTML_SPACE_INSIDE_EMPTY_TAG;
222 public boolean shouldSaveSpacesBetweenTagAndText() {
223 return true;