public void testTypingDoesNotInterfereWithDuplicates() throws Exception {
    SliceTreeStructure treeStructure = configureTree("DupSlice");
    SliceNode root = (SliceNode) treeStructure.getRootElement();
    List<SliceNode> nodes = new ArrayList<SliceNode>();
    expandNodesTo(root, nodes);

    for (int i = 0; i < nodes.size() - 1; i++) {
      SliceNode node = nodes.get(i);
      assertNull(node.getDuplicate());
    }
    SliceNode last = nodes.get(nodes.size() - 1);
    assertNotNull(last.getDuplicate());

    type("   xx");
    PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
    backspace();
    backspace();
    PsiDocumentManager.getInstance(getProject()).commitAllDocuments();

    nodes.clear();
    expandNodesTo(root, nodes);
    for (int i = 0; i < nodes.size() - 1; i++) {
      SliceNode node = nodes.get(i);
      assertNull(node.getDuplicate());
    }
    assertNotNull(last.getDuplicate());
  }
  private static PsiAnnotationMemberValue[] readFromClass(
      @NonNls String attributeName, @NotNull PsiAnnotation magic, PsiType type) {
    PsiAnnotationMemberValue fromClassAttr = magic.findAttributeValue(attributeName);
    PsiType fromClassType =
        fromClassAttr instanceof PsiClassObjectAccessExpression
            ? ((PsiClassObjectAccessExpression) fromClassAttr).getOperand().getType()
            : null;
    PsiClass fromClass =
        fromClassType instanceof PsiClassType ? ((PsiClassType) fromClassType).resolve() : null;
    if (fromClass == null) return null;
    String fqn = fromClass.getQualifiedName();
    if (fqn == null) return null;
    List<PsiAnnotationMemberValue> constants = new ArrayList<PsiAnnotationMemberValue>();
    for (PsiField field : fromClass.getFields()) {
      if (!field.hasModifierProperty(PsiModifier.PUBLIC)
          || !field.hasModifierProperty(PsiModifier.STATIC)
          || !field.hasModifierProperty(PsiModifier.FINAL)) continue;
      PsiType fieldType = field.getType();
      if (!Comparing.equal(fieldType, type)) continue;
      PsiAssignmentExpression e =
          (PsiAssignmentExpression)
              JavaPsiFacade.getElementFactory(field.getProject())
                  .createExpressionFromText("x=" + fqn + "." + field.getName(), field);
      PsiReferenceExpression refToField = (PsiReferenceExpression) e.getRExpression();
      constants.add(refToField);
    }
    if (constants.isEmpty()) return null;

    return constants.toArray(new PsiAnnotationMemberValue[constants.size()]);
  }
  private static void checkStructure(final SliceNode root, @NonNls String dataExpected) {
    List<SliceNode> actualNodes = new ArrayList<SliceNode>((Collection) root.getChildren());
    Collections.sort(actualNodes, SliceTreeBuilder.SLICE_NODE_COMPARATOR);

    Object[] actualStrings =
        ContainerUtil.map2Array(
            actualNodes,
            new Function<SliceNode, Object>() {
              @Override
              public Object fun(SliceNode node) {
                return node.toString();
              }
            });

    String[] childrenExpected =
        dataExpected.length() == 0 ? ArrayUtil.EMPTY_STRING_ARRAY : dataExpected.split("\n");
    String curChildren = "";
    String curNode = null;
    int iactual = 0;
    for (int iexp = 0; iexp <= childrenExpected.length; iexp++) {
      String e = iexp == childrenExpected.length ? null : childrenExpected[iexp];
      boolean isTopLevel = e == null || e.charAt(0) != ' ';
      if (isTopLevel) {
        if (curNode != null) {
          assertTrue(iactual < actualStrings.length);
          Object actual = actualStrings[iactual];
          assertEquals(curNode, actual);
          checkStructure(actualNodes.get(iactual), curChildren);
          iactual++;
        }

        curNode = e;
        curChildren = "";
      } else {
        curChildren += StringUtil.trimStart(e, "  ") + "\n";
      }
    }
    assertEquals(actualNodes.size(), iactual);
  }
  private static AllowedValues parseBeanInfo(@NotNull PsiModifierListOwner owner) {
    PsiMethod method = null;
    if (owner instanceof PsiParameter) {
      PsiParameter parameter = (PsiParameter) owner;
      PsiElement scope = parameter.getDeclarationScope();
      if (!(scope instanceof PsiMethod)) return null;
      PsiElement nav = scope.getNavigationElement();
      if (!(nav instanceof PsiMethod)) return null;
      method = (PsiMethod) nav;
      if (method.isConstructor()) {
        // not a property, try the @ConstructorProperties({"prop"})
        PsiAnnotation annotation =
            AnnotationUtil.findAnnotation(method, "java.beans.ConstructorProperties");
        if (annotation == null) return null;
        PsiAnnotationMemberValue value = annotation.findAttributeValue("value");
        if (!(value instanceof PsiArrayInitializerMemberValue)) return null;
        PsiAnnotationMemberValue[] initializers =
            ((PsiArrayInitializerMemberValue) value).getInitializers();
        PsiElement parent = parameter.getParent();
        if (!(parent instanceof PsiParameterList)) return null;
        int index = ((PsiParameterList) parent).getParameterIndex(parameter);
        if (index >= initializers.length) return null;
        PsiAnnotationMemberValue initializer = initializers[index];
        if (!(initializer instanceof PsiLiteralExpression)) return null;
        Object val = ((PsiLiteralExpression) initializer).getValue();
        if (!(val instanceof String)) return null;
        PsiMethod setter =
            PropertyUtil.findPropertySetter(
                method.getContainingClass(), (String) val, false, false);
        if (setter == null) return null;
        // try the @beaninfo of the corresponding setter
        method = (PsiMethod) setter.getNavigationElement();
      }
    } else if (owner instanceof PsiMethod) {
      PsiElement nav = owner.getNavigationElement();
      if (!(nav instanceof PsiMethod)) return null;
      method = (PsiMethod) nav;
    }
    if (method == null) return null;

    PsiClass aClass = method.getContainingClass();
    if (aClass == null) return null;
    if (PropertyUtil.isSimplePropertyGetter(method)) {
      List<PsiMethod> setters =
          PropertyUtil.getSetters(aClass, PropertyUtil.getPropertyNameByGetter(method));
      if (setters.size() != 1) return null;
      method = setters.get(0);
    }
    if (!PropertyUtil.isSimplePropertySetter(method)) return null;
    PsiDocComment doc = method.getDocComment();
    if (doc == null) return null;
    PsiDocTag beaninfo = doc.findTagByName("beaninfo");
    if (beaninfo == null) return null;
    String data =
        StringUtil.join(
            beaninfo.getDataElements(),
            new Function<PsiElement, String>() {
              @Override
              public String fun(PsiElement element) {
                return element.getText();
              }
            },
            "\n");
    int enumIndex = StringUtil.indexOfSubstringEnd(data, "enum:");
    if (enumIndex == -1) return null;
    data = data.substring(enumIndex);
    int colon = data.indexOf(":");
    int last = colon == -1 ? data.length() : data.substring(0, colon).lastIndexOf("\n");
    data = data.substring(0, last);

    List<PsiAnnotationMemberValue> values = new ArrayList<PsiAnnotationMemberValue>();
    for (String line : StringUtil.splitByLines(data)) {
      List<String> words = StringUtil.split(line, " ", true, true);
      if (words.size() != 2) continue;
      String ref = words.get(1);
      PsiExpression constRef =
          JavaPsiFacade.getElementFactory(aClass.getProject())
              .createExpressionFromText(ref, aClass);
      if (!(constRef instanceof PsiReferenceExpression)) continue;
      PsiReferenceExpression expr = (PsiReferenceExpression) constRef;
      values.add(expr);
    }
    if (values.isEmpty()) return null;
    PsiAnnotationMemberValue[] array = values.toArray(new PsiAnnotationMemberValue[values.size()]);
    return new AllowedValues(array, false);
  }