blob: 5c052f4beadec60ca5fb7d18ee44402c99e0cd0a [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 org.jetbrains.plugins.groovy.formatter;
import com.intellij.formatting.Alignment;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.util.containers.hash.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* @author Max Medvedev
*/
public class AlignmentProvider {
private final Map<PsiElement, Set<PsiElement>> myTree = new HashMap<PsiElement, Set<PsiElement>>();
private final Map<Set<PsiElement>, Alignment> myAlignments = new HashMap<Set<PsiElement>, Alignment>();
private final Map<Set<PsiElement>, Boolean> myAllowBackwardShift = new HashMap<Set<PsiElement>, Boolean>();
private final Map<Set<PsiElement>, Alignment.Anchor> myAnchor = new HashMap<Set<PsiElement>, Alignment.Anchor>();
public void addPair(@NotNull PsiElement e1, @NotNull PsiElement e2, @Nullable Boolean allowBackwardShift) {
addPair(e1, e2, allowBackwardShift, null);
}
public void addPair(@NotNull PsiElement e1, @NotNull PsiElement e2, @Nullable Boolean allowBackwardShift, @Nullable Alignment.Anchor anchor) {
assert e1 != e2;
final Set<PsiElement> set1 = myTree.get(e1);
final Set<PsiElement> set2 = myTree.get(e2);
if (set1 != null && set2 != null) {
assert (!myAlignments.containsKey(set1) || !myAlignments.containsKey(set2));
assert(myAllowBackwardShift.get(set1).booleanValue() == myAllowBackwardShift.get(set2).booleanValue());
assert(myAnchor.get(set1) == myAnchor.get(set2));
if (allowBackwardShift != null) {
assert(myAllowBackwardShift.get(set1).booleanValue() == allowBackwardShift.booleanValue());
}
if (anchor != null) {
assert(myAnchor.get(set1) == anchor);
}
if (myAlignments.containsKey(set2)) {
addSet(set1, set2);
}
else {
set1.addAll(set2);
addSet(set2, set1);
}
}
else if (set1 != null) {
addElement(e2, allowBackwardShift, anchor, set1);
}
else if (set2 != null) {
addElement(e1, allowBackwardShift, anchor, set2);
}
else {
final HashSet<PsiElement> set = createHashSet();
addInternal(set, e1);
addInternal(set, e2);
myAllowBackwardShift.put(set, allowBackwardShift);
myAnchor.put(set, anchor);
}
}
private void addElement(PsiElement e, Boolean allowBackwardShift, Alignment.Anchor anchor, Set<PsiElement> set) {
if (allowBackwardShift != null) {
assert myAllowBackwardShift.get(set).booleanValue() == allowBackwardShift.booleanValue();
}
if (anchor != null) {
assert myAnchor.get(set) == anchor;
}
addInternal(set, e);
}
private void addSet(Set<PsiElement> set1, Set<PsiElement> set2) {
for (Iterator<PsiElement> iterator = set1.iterator(); iterator.hasNext(); ) {
PsiElement element = iterator.next();
iterator.remove();
addInternal(set2, element);
}
}
private void addInternal(@NotNull Set<PsiElement> set, @NotNull PsiElement element) {
myTree.put(element, set);
set.add(element);
}
@NotNull
private static HashSet<PsiElement> createHashSet() {
return new HashSet<PsiElement>() {
private final int myhash = new Object().hashCode();
@Override
public int hashCode() {
return myhash;
}
};
}
public void addPair(@NotNull ASTNode node1, @NotNull ASTNode node2, boolean allowBackwardShift) {
addPair(node1.getPsi(), node2.getPsi(), allowBackwardShift);
}
private void add(@NotNull PsiElement element, boolean allowBackwardShift) {
add(element, allowBackwardShift, Alignment.Anchor.LEFT);
}
private void add(@NotNull PsiElement element, boolean allowBackwardShift, @NotNull Alignment.Anchor anchor) {
if (myTree.get(element) != null) return;
final HashSet<PsiElement> set = createHashSet();
set.add(element);
myTree.put(element, set);
myAllowBackwardShift.put(set, allowBackwardShift);
myAnchor.put(set, anchor);
}
@Nullable
public Alignment getAlignment(@NotNull PsiElement e) {
final Set<PsiElement> set = myTree.get(e);
if (set == null) {
return null;
}
Alignment alignment = myAlignments.get(set);
if (alignment != null) return alignment;
Alignment.Anchor anchor = myAnchor.get(set);
if (anchor == null) {
myAnchor.put(set, Alignment.Anchor.LEFT);
anchor = Alignment.Anchor.LEFT;
}
alignment = Alignment.createAlignment(myAllowBackwardShift.get(set), anchor);
myAlignments.put(set, alignment);
return alignment;
}
@NotNull
public Aligner createAligner(boolean allowBackwardShift) {
return new Aligner(allowBackwardShift, Alignment.Anchor.LEFT);
}
@NotNull
public Aligner createAligner(PsiElement element, boolean allowBackwardShift, Alignment.Anchor anchor) {
final Aligner aligner = new Aligner(allowBackwardShift, anchor);
aligner.append(element);
return aligner;
}
/**
* This class helps to assign one alignment to some elements.
* You can create an instance of Aligner and apply 'append' to any element, you want to be aligned.
*
* @author Max Medvedev
*/
public class Aligner {
private PsiElement myRef = null;
private boolean allowBackwardShift = true;
@NotNull
private final Alignment.Anchor myAnchor;
private Aligner(boolean allowBackwardShift, @NotNull Alignment.Anchor anchor) {
this.allowBackwardShift = allowBackwardShift;
myAnchor = anchor;
}
public void append(@Nullable PsiElement element) {
if (element == null) return;
if (myRef == null) {
myRef = element;
add(element, allowBackwardShift, myAnchor);
}
else {
addPair(myRef, element, allowBackwardShift, myAnchor);
}
}
}
}