Merge branch 'master' into uta/rainbow
[idea/community.git] / platform / analysis-impl / src / com / intellij / codeInsight / daemon / UsedColors.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.codeInsight.daemon;
17
18 import com.intellij.codeHighlighting.RainbowHighlighter;
19 import com.intellij.openapi.util.Key;
20 import com.intellij.openapi.util.UserDataHolderEx;
21 import com.intellij.util.ArrayUtil;
22 import org.jetbrains.annotations.NotNull;
23
24 import java.util.concurrent.atomic.AtomicInteger;
25
26 class UsedColors {
27   private static final Key<Object/*UsedColor or UsedColor[]*/> USED_COLOR = Key.create("USED_COLOR");
28
29   public static final AtomicInteger counter = new AtomicInteger();
30   private static class UsedColor {
31     @NotNull final String name;
32     final int index;
33
34     UsedColor(@NotNull String name, int index) {
35       this.name = name;
36       this.index = index;
37       counter.incrementAndGet();
38     }
39   }
40
41   static int getOrAddColorIndex(@NotNull final UserDataHolderEx context,
42                                 @NotNull final String name,
43                                 @NotNull RainbowHighlighter rainbowHighlighter) {
44     int colorsCount = rainbowHighlighter.getColorsCount();
45     Object data = context.getUserData(USED_COLOR);
46
47     int colorIndex;
48     while (true) {
49       Object newColors;
50       if (data == null) {
51         colorIndex = RainbowHighlighter.hashColor(name, colorsCount);
52         newColors = new UsedColor(name, colorIndex); // put an object instead of array to save space
53       }
54       else if (data instanceof UsedColor) {
55         UsedColor usedColor = (UsedColor)data;
56         if (usedColor.name.equals(name)) {
57           colorIndex = usedColor.index;
58           newColors = null; // found, no need to create new
59         }
60         else {
61           int hashedIndex = RainbowHighlighter.hashColor(name, colorsCount);
62           if (hashedIndex == usedColor.index) hashedIndex = (hashedIndex + 1) % colorsCount;
63           colorIndex = hashedIndex;
64           UsedColor newColor = new UsedColor(name, colorIndex);
65           newColors = new UsedColor[]{usedColor, newColor};
66         }
67       }
68       else {
69         colorIndex = -1;
70         int hashedIndex = RainbowHighlighter.hashColor(name, colorsCount);
71         int[] index2usage = new int[colorsCount];
72         UsedColor[] usedColors = (UsedColor[])data;
73         for (UsedColor usedColor : usedColors) {
74           int index = usedColor.index;
75           index2usage[index]++;
76           if (usedColor.name.equals(name)) {
77             colorIndex = index;
78             break;
79           }
80         }
81         if (colorIndex == -1) {
82           colorIndex = RainbowHighlighter.getColorIndex(index2usage, hashedIndex, colorsCount);
83           UsedColor newColor = new UsedColor(name, colorIndex);
84           newColors = ArrayUtil.append(usedColors, newColor);
85         }
86         else {
87           newColors = null;
88         }
89       }
90       if (newColors == null || context.replace(USED_COLOR, data, newColors)) {
91         break;
92       }
93     }
94
95     return colorIndex;
96   }
97 }