/*
 * Copyright 2000-2016 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.usages;

import com.intellij.codeInsight.highlighting.ReadWriteAccessDetector;
import com.intellij.codeInsight.highlighting.ReadWriteUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.SmartPointerManager;
import com.intellij.psi.SmartPsiElementPointer;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author Eugene Zhuravlev
 */
public class UsageInfoToUsageConverter {
  private UsageInfoToUsageConverter() {
  }

  public static class TargetElementsDescriptor {
    private final List<SmartPsiElementPointer<PsiElement>> myPrimarySearchedElements;
    private final List<SmartPsiElementPointer<PsiElement>> myAdditionalSearchedElements;

    public TargetElementsDescriptor(@NotNull PsiElement element) {
      this(new PsiElement[]{element});
    }

    public TargetElementsDescriptor(PsiElement @NotNull [] primarySearchedElements) {
      this(primarySearchedElements, PsiElement.EMPTY_ARRAY);
    }

    public TargetElementsDescriptor(PsiElement @NotNull [] primarySearchedElements, PsiElement @NotNull [] additionalSearchedElements) {
      myPrimarySearchedElements = convertToSmartPointers(primarySearchedElements);
      myAdditionalSearchedElements = convertToSmartPointers(additionalSearchedElements);
    }

    private static PsiElement @NotNull [] convertToPsiElements(@NotNull List<? extends SmartPsiElementPointer<PsiElement>> primary) {
      return ContainerUtil.toArray(ContainerUtil.mapNotNull(primary, SmartPsiElementPointer::getElement), PsiElement.ARRAY_FACTORY);
    }

    @NotNull
    private static List<SmartPsiElementPointer<PsiElement>> convertToSmartPointers(PsiElement @NotNull [] primaryElements) {
      if (primaryElements.length == 0) return Collections.emptyList();
  
      final SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(primaryElements[0].getProject());
      return ContainerUtil.mapNotNull(primaryElements, smartPointerManager::createSmartPsiElementPointer);
    }

    /**
     * A read-only attribute describing the target as a "primary" target.
     * A primary target is a target that was the main purpose of the search.
     * All usages of a non-primary target should be considered as a special case of usages of the corresponding primary target.
     * Example: searching field and its getter and setter methods -
     *          the field searched is a primary target, and its accessor methods are non-primary targets, because
     *          for this particular search usages of getter/setter methods are to be considered as a usages of the corresponding field.
     */
    public PsiElement @NotNull [] getPrimaryElements() {
      return convertToPsiElements(myPrimarySearchedElements);
    }

    public PsiElement @NotNull [] getAdditionalElements() {
      return convertToPsiElements(myAdditionalSearchedElements);
    }

    @NotNull
    public List<PsiElement> getAllElements() {
      List<PsiElement> result = new ArrayList<>(myPrimarySearchedElements.size() + myAdditionalSearchedElements.size());
      for (SmartPsiElementPointer pointer : myPrimarySearchedElements) {
        PsiElement element = pointer.getElement();
        if (element != null) {
          result.add(element);
        }
      }
      for (SmartPsiElementPointer pointer : myAdditionalSearchedElements) {
        PsiElement element = pointer.getElement();
        if (element != null) {
          result.add(element);
        }
      }
      return result;
    }

    @NotNull
    public List<SmartPsiElementPointer<PsiElement>> getAllElementPointers() {
      return ContainerUtil.concat(myPrimarySearchedElements, myAdditionalSearchedElements);
    }
  }

  @NotNull
  public static Usage convert(@NotNull TargetElementsDescriptor descriptor, @NotNull UsageInfo usageInfo) {
    PsiElement[] primaryElements = descriptor.getPrimaryElements();

    return convert(primaryElements, usageInfo);
  }

  @NotNull
  public static Usage convert(PsiElement @NotNull [] primaryElements, @NotNull UsageInfo usageInfo) {
    PsiElement usageElement = usageInfo.getElement();
    if (usageElement != null && primaryElements.length != 0) {
      ReadWriteAccessDetector.Access rwAccess = ReadWriteUtil.getReadWriteAccess(primaryElements, usageElement);
      if (rwAccess != null) {
        return new ReadWriteAccessUsageInfo2UsageAdapter(usageInfo,
                                                         rwAccess != ReadWriteAccessDetector.Access.Write,
                                                         rwAccess != ReadWriteAccessDetector.Access.Read);
      }
    }
    return new UsageInfo2UsageAdapter(usageInfo);
  }

  public static Usage @NotNull [] convert(@NotNull TargetElementsDescriptor descriptor, UsageInfo @NotNull [] usageInfos) {
    Usage[] usages = new Usage[usageInfos.length];
    for (int i = 0; i < usages.length; i++) {
      usages[i] = convert(descriptor, usageInfos[i]);
    }
    return usages;
  }


  public static Usage @NotNull [] convert(final PsiElement @NotNull [] primaryElements, UsageInfo @NotNull [] usageInfos) {
    return ContainerUtil.map(usageInfos, info -> convert(primaryElements, info), new Usage[usageInfos.length]);
  }
}

@@ -1,13 +1,10 @@
 /
-*%0A *
+/
  Copyrig
@@ -17,10 +17,10 @@
 0-20
-16
+20
  Jet
@@ -36,566 +36,109 @@
 r.o.
-%0A *%0A * Licensed under the Apache License, Version 2.0 (the %22License%22);%0A * you may not use this file except in compliance with the License.%0A * You may obtain a copy of the License at%0A *%0A * http://www.apache.org/licenses/LICENSE-2.0%0A *%0A * Unless required by applicable law or agreed to in writing, software%0A * distributed under the License is distributed on an %22AS IS%22 BASIS,%0A * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.%0A * See the License for the specific language governing permissions and%0A * limitations under the License.%0A */
+ Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 %0Apac
@@ -1128,32 +1128,41 @@
 mentsDescriptor(
+@NotNull 
 PsiElement @NotN
@@ -1299,16 +1299,25 @@
 criptor(
+@NotNull 
 PsiEleme
@@ -1347,32 +1347,77 @@
 earchedElements,
+%0A                                    @NotNull
  PsiElement @Not
@@ -1652,16 +1652,25 @@
  static 
+@NotNull 
 PsiEleme
@@ -1945,32 +1945,41 @@
 ate static List%3C
+@NotNull 
 SmartPsiElementP
@@ -2021,16 +2021,25 @@
 ointers(
+@NotNull 
 PsiEleme
@@ -2147,11 +2147,8 @@
 ();%0A
-  %0A
     
@@ -2738,25 +2738,16 @@
 -%0A     *
-         
  the fie
@@ -2843,25 +2843,16 @@
 e%0A     *
-         
  for thi
@@ -2975,32 +2975,41 @@
   */%0A    public 
+@NotNull 
 PsiElement @NotN
@@ -3118,16 +3118,25 @@
  public 
+@NotNull 
 PsiEleme

mark component types as @NotNull in UsageInfoToUsageConverter$TargetElementsDescriptor