blob: 39b5ae7fb07241056a82192b8a6ec46c4ca78000 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.formatter.xml;
import com.intellij.formatting.*;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.formatter.common.AbstractBlock;
import com.intellij.psi.impl.source.SourceTreeToPsiMap;
import com.intellij.psi.templateLanguages.OuterLanguageElement;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.xml.XmlElementType;
import com.intellij.psi.xml.XmlTag;
import com.intellij.psi.xml.XmlTokenType;
import java.util.List;
public abstract class AbstractSyntheticBlock implements Block {
protected final Indent myIndent;
protected final XmlFormattingPolicy myXmlFormattingPolicy;
protected final ASTNode myEndTreeNode;
protected final ASTNode myStartTreeNode;
private final XmlTag myTag;
public AbstractSyntheticBlock(List<Block> subBlocks, Block parent, XmlFormattingPolicy policy, Indent indent) {
myEndTreeNode = getLastNode(subBlocks);
myStartTreeNode = getFirstNode(subBlocks);
myIndent = indent;
myXmlFormattingPolicy = policy;
if (parent instanceof AbstractXmlBlock) {
myTag = ((AbstractXmlBlock)parent).getTag();
}
else if (parent instanceof AbstractSyntheticBlock) {
myTag = ((AbstractSyntheticBlock)parent).getTag();
} else {
throw new IllegalStateException("Parent should be AbstractXmlBlock or AbstractSyntheticBlock, but it is " + parent.getClass());
}
}
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.formatter.xml.AbstractSyntheticBlock");
private ASTNode getFirstNode(final List<Block> subBlocks) {
LOG.assertTrue(!subBlocks.isEmpty());
final Block firstBlock = subBlocks.get(0);
if (firstBlock instanceof AbstractBlock) {
return ((AbstractBlock)firstBlock).getNode();
}
else {
return getFirstNode(firstBlock.getSubBlocks());
}
}
private ASTNode getLastNode(final List<Block> subBlocks) {
LOG.assertTrue(!subBlocks.isEmpty());
final Block lastBlock = subBlocks.get(subBlocks.size() - 1);
if (lastBlock instanceof AbstractBlock) {
return ((AbstractBlock)lastBlock).getNode();
}
else {
return getLastNode(lastBlock.getSubBlocks());
}
}
private boolean isEndOfTag() {
return myEndTreeNode.getElementType() == XmlTokenType.XML_TAG_END;
}
@Override
public Wrap getWrap() {
return null;
}
@Override
public Indent getIndent() {
return myIndent;
}
@Override
public Alignment getAlignment() {
return null;
}
protected static boolean isXmlTagName(final IElementType type1, final IElementType type2) {
if (type1 == XmlTokenType.XML_NAME && type2 == XmlTokenType.XML_TAG_END) return true;
if (type1 == XmlTokenType.XML_NAME && type2 == XmlTokenType.XML_EMPTY_ELEMENT_END) return true;
if (type1 == XmlElementType.XML_ATTRIBUTE && type2 == XmlTokenType.XML_EMPTY_ELEMENT_END) return true;
return type1 == XmlElementType.XML_ATTRIBUTE && type2 == XmlTokenType.XML_TAG_END;
}
public boolean endsWithText() {
return myEndTreeNode.getElementType() == XmlElementType.XML_TEXT ||
myEndTreeNode.getElementType() == XmlTokenType.XML_DATA_CHARACTERS;
}
public boolean isTagDescription() {
final ASTNode startTreeNode = myStartTreeNode;
return isTagDescription(startTreeNode);
}
private static boolean isTagDescription(final ASTNode startTreeNode) {
return startTreeNode.getElementType() == XmlTokenType.XML_START_TAG_START ||
startTreeNode.getElementType() == XmlTokenType.XML_END_TAG_START;
}
public boolean startsWithText() {
return myStartTreeNode.getElementType() == XmlElementType.XML_TEXT ||
myStartTreeNode.getElementType() == XmlTokenType.XML_DATA_CHARACTERS;
}
public boolean endsWithTextElement() {
if (endsWithText()) return true;
if (isEndOfTag() && myXmlFormattingPolicy.isTextElement(getTag())) return true;
return isTextTag(myEndTreeNode);
}
protected XmlTag getTag() {
return myTag;
}
public boolean startsWithTextElement() {
if (startsWithText()) return true;
if (isStartOfTag() && myXmlFormattingPolicy.isTextElement(getTag())) return true;
return isTextTag(myStartTreeNode);
}
private boolean isTextTag(final ASTNode treeNode) {
return isXmlTag(treeNode) && myXmlFormattingPolicy.isTextElement((XmlTag)SourceTreeToPsiMap.treeElementToPsi(treeNode));
}
private boolean isXmlTag(final ASTNode treeNode) {
return (treeNode.getPsi() instanceof XmlTag);
}
private boolean isStartOfTag() {
return isTagDescription(myStartTreeNode);
}
protected static TextRange calculateTextRange(final List<Block> subBlocks) {
return new TextRange(subBlocks.get(0).getTextRange().getStartOffset(),
subBlocks.get(subBlocks.size() - 1).getTextRange().getEndOffset());
}
@Override
public boolean isIncomplete() {
return getSubBlocks().get(getSubBlocks().size() - 1).isIncomplete();
}
public boolean startsWithTag() {
return isXmlTag(myStartTreeNode);
}
public XmlTag getStartTag() {
return (XmlTag)myStartTreeNode.getPsi();
}
public boolean endsWithTag() {
return isXmlTag(myEndTreeNode);
}
public boolean isJspTextBlock() {
return false;
}
public boolean isJspxTextBlock() {
return false;
}
/**
* Checks if the block contains a single node which belongs to the outer (template) language.
*
* @return True if it does, False otherwise.
*/
public boolean isOuterLanguageBlock() {
return (myStartTreeNode == myEndTreeNode) && (myStartTreeNode instanceof OuterLanguageElement);
}
@Override
public boolean isLeaf() {
return false;
}
public boolean startsWithCDATA() {
return isCDATA(myStartTreeNode.getFirstChildNode());
}
private boolean isCDATA(final ASTNode node) {
return node != null && node.getElementType() == XmlElementType.XML_CDATA;
}
public boolean containsCDATA() {
return myStartTreeNode.getElementType() == XmlTokenType.XML_CDATA_START &&
myEndTreeNode.getElementType() == XmlTokenType.XML_CDATA_END;
}
public boolean endsWithCDATA() {
return isCDATA(myStartTreeNode.getLastChildNode());
}
public boolean insertLineFeedAfter() {
final List<Block> subBlocks = getSubBlocks();
final Block lastBlock = subBlocks.get(subBlocks.size() - 1);
if (lastBlock instanceof XmlTagBlock) {
return insertLineFeedAfter(((XmlTagBlock)lastBlock).getTag());
}
if (endsWithText()) {
return insertLineFeedAfter(myTag);
}
return false;
}
protected boolean insertLineFeedAfter(final XmlTag tag) {
return myXmlFormattingPolicy.getWrappingTypeForTagBegin(tag) == WrapType.ALWAYS;
}
}