/*
* Copyright 2000-2012 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.formatting.templateLanguages;
import com.intellij.formatting.*;
import com.intellij.lang.ASTNode;
import com.intellij.lang.Language;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.formatter.common.AbstractBlock;
import com.intellij.psi.templateLanguages.OuterLanguageElement;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author Alexey Chmutov
* Date: Jun 30, 2009
* Time: 7:18:37 PM
*/
public class DataLanguageBlockWrapper implements ASTBlock, BlockEx, BlockWithParent {
private final Block myOriginal;
@Nullable private final Language myLanguage;
private List<Block> myBlocks;
private List<TemplateLanguageBlock> myTlBlocks;
private BlockWithParent myParent;
private DataLanguageBlockWrapper myRightHandWrapper;
private Spacing mySpacing;
private Map<Pair<Block, Block>, Spacing> myChildDataBorderSpacings;
private DataLanguageBlockWrapper(@NotNull final Block original) {
assert !(original instanceof DataLanguageBlockWrapper) && !(original instanceof TemplateLanguageBlock);
myOriginal = original;
final ASTNode node = getNode();
Language language = null;
if (node != null) {
final PsiElement psi = node.getPsi();
if (psi != null) {
language = psi.getContainingFile().getLanguage();
}
}
myLanguage = language;
}
@Override
@NotNull
public TextRange getTextRange() {
return myOriginal.getTextRange();
}
@Override
@NotNull
public List<Block> getSubBlocks() {
if (myBlocks == null) {
myBlocks = buildBlocks();
initSpacings();
}
return myBlocks;
}
private void initSpacings() {
for (int i = 1; i < myBlocks.size(); i++) {
Block block1 = myBlocks.get(i - 1);
Block block2 = myBlocks.get(i);
if (block1 instanceof TemplateLanguageBlock) {
Spacing spacing = ((TemplateLanguageBlock)block1).getRightNeighborSpacing(block2, this, i - 1);
if (spacing != null) {
if (myChildDataBorderSpacings == null) {
myChildDataBorderSpacings = ContainerUtil.newTroveMap();
}
myChildDataBorderSpacings.put(Pair.create(block1, block2), spacing);
}
}
}
}
@Nullable
@Override
public Language getLanguage() {
// Use base language code style settings for the template blocks.
return myLanguage;
}
private List<Block> buildBlocks() {
assert myBlocks == null;
if (isLeaf()) {
return AbstractBlock.EMPTY;
}
final List<DataLanguageBlockWrapper> subWrappers = BlockUtil.buildChildWrappers(myOriginal);
final List<Block> children;
if (myTlBlocks == null) {
children = new ArrayList<>(subWrappers);
}
else if (subWrappers.size() == 0) {
//noinspection unchecked
children = (List<Block>)(subWrappers.size() > 0 ? myTlBlocks : BlockUtil.splitBlockIntoFragments(myOriginal, myTlBlocks));
}
else {
children = BlockUtil.mergeBlocks(myTlBlocks, subWrappers);
}
//BlockUtil.printBlocks(getTextRange(), children);
return BlockUtil.setParent(children, this);
}
@Override
public Wrap getWrap() {
BlockWithParent parent = getParent();
if (parent instanceof TemplateLanguageBlock) {
return ((TemplateLanguageBlock)parent).substituteTemplateChildWrap(this, myOriginal.getWrap());
}
return myOriginal.getWrap();
}
@Override
@NotNull
public ChildAttributes getChildAttributes(final int newChildIndex) {
return myOriginal.getChildAttributes(newChildIndex);
}
@Override
public Indent getIndent() {
return myOriginal.getIndent();
}
@Override
public Alignment getAlignment() {
return myOriginal.getAlignment();
}
@Override
@Nullable
public Spacing getSpacing(Block child1, @NotNull Block child2) {
if (child1 instanceof DataLanguageBlockWrapper && child2 instanceof DataLanguageBlockWrapper) {
return myOriginal.getSpacing(((DataLanguageBlockWrapper)child1).myOriginal, ((DataLanguageBlockWrapper)child2).myOriginal);
}
if (child1 instanceof TemplateLanguageBlock && myChildDataBorderSpacings != null) {
return myChildDataBorderSpacings.get(Pair.create(child1, child2));
}
return null;
}
@Override
public boolean isIncomplete() {
return myOriginal.isIncomplete();
}
@Override
public boolean isLeaf() {
return myTlBlocks == null && myOriginal.isLeaf();
}
void addTlChild(TemplateLanguageBlock tlBlock) {
assert myBlocks == null;
if (myTlBlocks == null) {
myTlBlocks = new ArrayList<>(5);
}
myTlBlocks.add(tlBlock);
tlBlock.setParent(this);
}
public Block getOriginal() {
return myOriginal;
}
@Override
public String toString() {
String tlBlocksInfo = " TlBlocks " + (myTlBlocks == null ? "0" : myTlBlocks.size()) + "|" + getTextRange() + "|";
return tlBlocksInfo + myOriginal.toString();
}
@Nullable
public static DataLanguageBlockWrapper create(@NotNull final Block original, @Nullable final Indent indent) {
final boolean doesntNeedWrapper = original instanceof ASTBlock && ((ASTBlock)original).getNode() instanceof OuterLanguageElement;
return doesntNeedWrapper ? null : new DataLanguageBlockWrapper(original);
}
@Override
@Nullable
public ASTNode getNode() {
return myOriginal instanceof ASTBlock ? ((ASTBlock)myOriginal).getNode() : null;
}
@Override
public BlockWithParent getParent() {
return myParent;
}
@Override
public void setParent(BlockWithParent parent) {
myParent = parent;
}
public void setRightHandSpacing(DataLanguageBlockWrapper rightHandWrapper, Spacing spacing) {
myRightHandWrapper = rightHandWrapper;
mySpacing = spacing;
}
@Nullable
public Spacing getRightHandSpacing(DataLanguageBlockWrapper rightHandWrapper) {
return myRightHandWrapper == rightHandWrapper ? mySpacing : null;
}
}