/*
 * Copyright 2000-2017 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.intellij.lang.regexp.inspection;

import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.tree.IElementType;
import org.intellij.lang.regexp.RegExpTT;
import org.intellij.lang.regexp.psi.*;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;

/**
 * @author Bas Leijdekkers
 */
public class SingleCharAlternationInspection extends LocalInspectionTool {

  @Nls
  @NotNull
  @Override
  public String getDisplayName() {
    return "Single character alternation";
  }

  @NotNull
  @Override
  public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
    return new SingleCharAlternationVisitor(holder);
  }

  private static class SingleCharAlternationVisitor extends RegExpElementVisitor {

    private final ProblemsHolder myHolder;

    public SingleCharAlternationVisitor(ProblemsHolder holder) {
      myHolder = holder;
    }

    @Override
    public void visitRegExpPattern(RegExpPattern pattern) {
      final RegExpBranch[] branches = pattern.getBranches();
      if (branches.length < 2) {
        return;
      }
      for (RegExpBranch branch : branches) {
        final RegExpAtom[] atoms = branch.getAtoms();
        if (atoms.length != 1 || !(atoms[0] instanceof RegExpChar)) {
          return;
        }
      }
      final String text = buildReplacementText(pattern);
      //noinspection DialogTitleCapitalization
      myHolder.registerProblem(pattern, "Single character alternation in RegExp", new SingleCharAlternationFix(text));
    }

    private static class SingleCharAlternationFix implements LocalQuickFix {

      private final String myText;

      public SingleCharAlternationFix(String text) {
        myText = text;
      }

      @Nls
      @NotNull
      @Override
      public String getName() {
        return "Replace with '" + myText + '\'';
      }

      @Nls
      @NotNull
      @Override
      public String getFamilyName() {
        return "Replace alternation with character class";
      }

      @Override
      public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
        final PsiElement element = descriptor.getPsiElement();
        if (!(element instanceof RegExpPattern)) {
          return;
        }
        final RegExpPattern pattern = (RegExpPattern)element;
        final String text = buildReplacementText(pattern);
        final RegExpBranch branch = RegExpFactory.createBranchFromText(text, element);
        pattern.replace(branch.getAtoms()[0]);
      }
    }
  }

  static String buildReplacementText(RegExpPattern pattern) {
    final StringBuilder text = new StringBuilder("[");
    for (RegExpBranch branch : pattern.getBranches()) {
      for (PsiElement child : branch.getChildren()) {
        final RegExpChar ch = (RegExpChar)child;
        final IElementType type = ch.getNode().getFirstChildNode().getElementType();
        if (type == RegExpTT.REDUNDANT_ESCAPE) {
          text.append((char)ch.getValue());
        }
        else if (type == RegExpTT.ESC_CHARACTER) {
          final int value = ch.getValue();
          switch (value) {
            case '.':
            case '$':
            case '?':
            case '*':
            case '+':
            case '|':
            case '{':
            case '(':
            case ')':
              text.append((char)value);
              break;
            case '^':
              if (text.length() == 1) {
                text.append(ch.getText());
              }
              else {
                text.append((char)value);
              }
              break;
            default:
              text.append(ch.getText());
          }
        }
        else {
          text.append(ch.getText());
        }
      }
    }
    text.append("]");
    return text.toString();
  }
}

@@ -1180,16 +1180,49 @@
 tNull;%0A%0A
+import java.util.stream.Stream;%0A%0A
 /**%0A * @
@@ -2039,162 +2039,80 @@
     
-for (RegExpBranch branch : branches) %7B%0A        final RegExpAtom%5B%5D atoms = branch.getAtoms();%0A        if (atoms.length != 1 %7C%7C !(atoms%5B0%5D instanceof RegExp
+if (!Stream.of(branches).allMatch(SingleCharAlternationVisitor::isSingle
 Char
@@ -2112,26 +2112,24 @@
 gleChar)) %7B%0A
-  
         retu
@@ -2128,34 +2128,24 @@
     return;%0A
-        %7D%0A
       %7D%0A    
@@ -2366,24 +2366,212 @@
 t));%0A    %7D%0A%0A
+    private static boolean isSingleChar(RegExpBranch branch) %7B%0A      final RegExpAtom%5B%5D atoms = branch.getAtoms();%0A      return atoms.length == 1 && atoms%5B0%5D instanceof RegExpChar;%0A    %7D%0A%0A
     private 

use stream