private static List analyzeImpl()

in java/java.hints/src/org/netbeans/modules/java/hints/errors/CreateElement.java [148:505]


    private static List<Fix> analyzeImpl(CompilationInfo info, String diagnosticKey, int offset) throws IOException {
        if ("compiler.err.invalid.mref".equals(diagnosticKey)) {
            return computeMissingMemberRefFixes(info, offset);
        }

        TreePath errorPath = ErrorHintsProvider.findUnresolvedElement(info, offset);

        if (errorPath == null) {
            return Collections.<Fix>emptyList();
        }
        
        if (CAST_KEY.equals(diagnosticKey) && errorPath.getParentPath() != null && errorPath.getParentPath().getLeaf().getKind() == Kind.METHOD_INVOCATION) {
            MethodInvocationTree mit = (MethodInvocationTree) errorPath.getParentPath().getLeaf();
            errorPath = new TreePath(errorPath.getParentPath(), mit.getMethodSelect());
            offset = (int) info.getTrees().getSourcePositions().getStartPosition(errorPath.getCompilationUnit(), errorPath.getLeaf());
        }

        if (info.getElements().getTypeElement("java.lang.Object") == null) { // NOI18N
            // broken java platform
            return Collections.<Fix>emptyList();
        }

        TreePath parent = null;
        TreePath firstClass = null;
        TreePath firstMethod = null;
        TreePath firstVar = null;
        TreePath firstInitializer = null;
        TreePath methodInvocation = null;
        TreePath newClass = null;
        boolean baseType = false;
        boolean lookupMethodInvocation = true;
        boolean lookupNCT = true;

        TreePath path = info.getTreeUtilities().pathFor(Math.max((int) info.getTrees().getSourcePositions().getStartPosition(info.getCompilationUnit(), errorPath.getLeaf()), offset) + 1);

        while(path != null) {
            Tree leaf = path.getLeaf();
            Kind leafKind = leaf.getKind();

            if (!baseType && TreeUtilities.CLASS_TREE_KINDS.contains(leafKind) && parent != null && (((ClassTree)leaf).getExtendsClause() == parent.getLeaf() || ((ClassTree)leaf).getImplementsClause().contains(parent.getLeaf())))
                baseType = true;
            if (parent != null && parent.getLeaf() == errorPath.getLeaf())
                parent = path;
            if (leaf == errorPath.getLeaf() && parent == null)
                parent = path;
            if (TreeUtilities.CLASS_TREE_KINDS.contains(leafKind) && firstClass == null)
                firstClass = path;
            if (leafKind == Kind.METHOD && firstMethod == null && firstClass == null)
                firstMethod = path;
            //static/dynamic initializer:
            if (   leafKind == Kind.BLOCK && TreeUtilities.CLASS_TREE_KINDS.contains(path.getParentPath().getLeaf().getKind())
                && firstMethod == null && firstClass == null)
                firstInitializer = path;
            
            if (leafKind == Kind.ANNOTATION) {
                // discard any methods, since there cannot be method calls in anno values
                methodInvocation = null;
                lookupMethodInvocation = false;
            }

            if (lookupMethodInvocation && leafKind == Kind.METHOD_INVOCATION) {
                methodInvocation = path;
            }

            if (leafKind == Kind.VARIABLE) {
                firstVar = path;
            }
            
            if (lookupNCT && leafKind == Kind.NEW_CLASS) {
                newClass = path;
            }

            if (leafKind == Kind.MEMBER_SELECT) {
                lookupMethodInvocation = leaf == errorPath.getLeaf();
            }

            if (leafKind != Kind.MEMBER_SELECT && leafKind != Kind.IDENTIFIER) {
                lookupMethodInvocation = false;
            }

            if (leafKind != Kind.MEMBER_SELECT && leafKind != Kind.IDENTIFIER && leafKind != Kind.PARAMETERIZED_TYPE) {
                lookupNCT = false;
            }

            path = path.getParentPath();
        }

        if (parent == null || parent.getLeaf() == errorPath.getLeaf())
            return Collections.<Fix>emptyList();

        Element e = info.getTrees().getElement(errorPath);

        if (e == null) {
            return Collections.<Fix>emptyList();
        }

        Set<Modifier> modifiers = EnumSet.noneOf(Modifier.class);
        Name name = e.getSimpleName();
        if (name == null) {
            if (ErrorHintsProvider.ERR.isLoggable(ErrorManager.INFORMATIONAL)) {
                ErrorHintsProvider.ERR.log(ErrorManager.INFORMATIONAL, "e.simpleName=null"); // NOI18N
                ErrorHintsProvider.ERR.log(ErrorManager.INFORMATIONAL, "offset=" + offset); // NOI18N
                ErrorHintsProvider.ERR.log(ErrorManager.INFORMATIONAL, "errorTree=" + errorPath.getLeaf()); // NOI18N
            }

            return Collections.<Fix>emptyList();
        }
        String simpleName = name.toString();
        final TypeElement source = firstClass != null ? (TypeElement) info.getTrees().getElement(firstClass) : null;
        Element target = null;
        boolean wasMemberSelect = false;

        if (errorPath.getLeaf().getKind() == Kind.MEMBER_SELECT) {
            TreePath exp = new TreePath(errorPath, ((MemberSelectTree) errorPath.getLeaf()).getExpression());
            TypeMirror targetType = info.getTrees().getTypeMirror(exp);

            if (targetType != null) {
                if (targetType.getKind() == TypeKind.DECLARED) {
                    Element expElement = info.getTrees().getElement(exp);

                    if (isClassLikeElement(expElement)) {
                        modifiers.add(Modifier.STATIC);
                    }

                    Element targetElement = info.getTypes().asElement(targetType);

                    if (isClassLikeElement(targetElement)) {
                        target = (TypeElement) targetElement;
                    }
                } else if (targetType.getKind() == TypeKind.PACKAGE) {
                    target = info.getTrees().getElement(exp);
                }
            }

            wasMemberSelect = true;
        } else {
	    Element enclosingElement = e.getEnclosingElement();
	    if(enclosingElement != null && enclosingElement.getKind() == ElementKind.ANNOTATION_TYPE) //unresolved element inside annot.
			target = enclosingElement;
	    else

		if (errorPath.getLeaf().getKind() == Kind.IDENTIFIER) {
		    //TODO: Handle Annotations
                target = source;

                if (firstMethod != null) {
                    if (((MethodTree)firstMethod.getLeaf()).getModifiers().getFlags().contains(Modifier.STATIC)) {
                        modifiers.add(Modifier.STATIC);
                    }
                } else if (firstInitializer != null) {
                    if (((BlockTree) firstInitializer.getLeaf()).isStatic()) {
                        modifiers.add(Modifier.STATIC);
                    }
                } else if (firstVar != null && ((VariableTree)firstVar.getLeaf()).getModifiers().getFlags().contains(Modifier.STATIC)) {
                    modifiers.add(Modifier.STATIC);
                }
            }
        }

        if (target == null) {
            if (ErrorHintsProvider.ERR.isLoggable(ErrorManager.INFORMATIONAL)) {
                ErrorHintsProvider.ERR.log(ErrorManager.INFORMATIONAL, "target=null"); // NOI18N
                ErrorHintsProvider.ERR.log(ErrorManager.INFORMATIONAL, "offset=" + offset); // NOI18N
                ErrorHintsProvider.ERR.log(ErrorManager.INFORMATIONAL, "errorTree=" + errorPath.getLeaf()); // NOI18N
            }

            return Collections.<Fix>emptyList();
        }

        if (target instanceof TypeElement)
            modifiers.addAll(Utilities.getAccessModifiers(info, source, (TypeElement) target).getRequiredModifiers());
        else
            modifiers.add(Modifier.PUBLIC);

        List<Fix> result = new ArrayList<Fix>();

        if (methodInvocation != null) {
            //create method:
            MethodInvocationTree mit = (MethodInvocationTree) methodInvocation.getLeaf();
            //return type:
            Set<ElementKind> fixTypes = EnumSet.noneOf(ElementKind.class);
            List<? extends TypeMirror> types = resolveType(fixTypes, info, methodInvocation.getParentPath(), methodInvocation.getLeaf(), offset, null, null);

            if (types == null || types.isEmpty()) {
                return Collections.<Fix>emptyList();
            }
            try {
                result.addAll(prepareCreateMethodFix(info, methodInvocation, modifiers, (TypeElement) target, simpleName, mit.getArguments(), types));
            } catch (IllegalArgumentException ex) {
                // FIXME: see issue #243028; EXECUTABLE somehow gets here and causes an exception. Let's log all the necessary info incl. source
                LOG.log(Level.INFO, "Unexpected exception, perhaps a type that cannot be converted to a Handle. See issue #243028 for more details. Please attach" +
                        "the following ide.log to the issue");
                LOG.log(Level.INFO, "Caused by source:\n==============\n" + 
                                    info.getSnapshot().getText().toString() + "\n==============\n");
                LOG.log(Level.INFO, "Caused by error at offset " + offset + ", tree: " + methodInvocation.getLeaf().toString(), ex);
                LOG.log(Level.INFO, "Invocation starts at " + info.getTrees().getSourcePositions().getStartPosition(info.getCompilationUnit(), methodInvocation.getLeaf()));
                throw ex;
            }
        }

        Set<ElementKind> fixTypes = EnumSet.noneOf(ElementKind.class);
        TypeMirror[] superType = new TypeMirror[1];
        int[] numTypeParameters = new int[1];
        List<TypeMirror> types = (List<TypeMirror>)resolveType(fixTypes, info, parent, errorPath.getLeaf(), offset, superType, numTypeParameters);
        ElementKind classType = getClassType(fixTypes);
        
        if (!ErrorFixesFakeHint.enabled(info.getFileObject(), FixKind.CREATE_LOCAL_VARIABLE)) {
            fixTypes.remove(ElementKind.LOCAL_VARIABLE);
        }
        final TypeMirror type;
        
        if (types != null && !types.isEmpty()) {
            List<TypeMirror> resolvedTypes = null;
            int i = 0;
            for (Iterator<TypeMirror> it = types.iterator(); it.hasNext(); ) {
                final TypeMirror t = it.next();
                final TypeMirror resolved = Utilities.resolveTypeForDeclaration(info, t);
                if (resolved != t) {
                    if (resolvedTypes == null) {
                        resolvedTypes = new ArrayList(types);
                        types = resolvedTypes;
                    }
                    resolvedTypes.set(i, resolved);
                }
                i++;
            }
            //XXX: should reasonably consider all the found type candidates, not only the one:
            type = types.get(0);
            
            if (superType[0] == null) {
                // the type must be already un-captured.
                superType[0] = type;
            }
        } else {
            type = null;
        }

        if (target.getKind() == ElementKind.PACKAGE) {
            result.addAll(prepareCreateOuterClassFix(info, null, target, modifiers, simpleName, null, superType[0], classType != null ? classType : ElementKind.CLASS, numTypeParameters[0]));
            return result;
        }
        
        TypeElement outermostTypeElement = source != null ? info.getElementUtilities().outermostTypeElement(source) : null;

        if (newClass != null) {
            NewClassTree nct = (NewClassTree) newClass.getLeaf();
            Element clazz = info.getTrees().getElement(new TreePath(newClass, nct.getIdentifier()));

            if (clazz == null || clazz.asType().getKind() == TypeKind.ERROR || (!clazz.getKind().isClass() && !clazz.getKind().isInterface())) {
                //the class does not exist...
                ExpressionTree ident = nct.getIdentifier();
                int numTypeArguments = 0;

                if (ident.getKind() == Kind.PARAMETERIZED_TYPE) {
                    numTypeArguments = ((ParameterizedTypeTree) ident).getTypeArguments().size();
                }

                if (wasMemberSelect) {
                    return prepareCreateInnerClassFix(info, newClass, (TypeElement) target, modifiers, simpleName, nct.getArguments(), type, ElementKind.CLASS, numTypeArguments);
                } else {
		    List<Fix> currentResult = new LinkedList<Fix>();

		    currentResult.addAll(prepareCreateOuterClassFix(info, newClass, source, EnumSet.of(Modifier.PUBLIC), simpleName, nct.getArguments(), type, ElementKind.CLASS, numTypeArguments));
                    if (!baseType || outermostTypeElement != source)
		        currentResult.addAll(prepareCreateInnerClassFix(info, newClass, outermostTypeElement, EnumSet.of(outermostTypeElement != null && outermostTypeElement.getKind().isInterface() ? Modifier.PUBLIC : Modifier.PRIVATE, Modifier.STATIC), simpleName, nct.getArguments(), type, ElementKind.CLASS, numTypeArguments));
		    
                    return currentResult;
                }
            }

            if (nct.getClassBody() != null) {
                return Collections.<Fix>emptyList();
            }

            TypeElement clazzTarget = (TypeElement) clazz;

            result.addAll(prepareCreateMethodFix(info, newClass, Utilities.getAccessModifiers(info, source, clazzTarget).getRequiredModifiers(), clazzTarget, "<init>", nct.getArguments(), null)); //NOI18N
        }

        //field like or class (type):
        if (classType != null && e.asType().getKind() == TypeKind.ERROR) {
            if (wasMemberSelect) {
                 result.addAll(prepareCreateInnerClassFix(info, null, (TypeElement) target, modifiers, simpleName, null, superType[0], classType, numTypeParameters[0]));
            } else {
                result.addAll(prepareCreateOuterClassFix(info, null, source, EnumSet.noneOf(Modifier.class), simpleName, null, superType[0], classType, numTypeParameters[0]));
                if (!baseType || outermostTypeElement != source)
                    result.addAll(prepareCreateInnerClassFix(info, null, outermostTypeElement, EnumSet.of(outermostTypeElement != null && outermostTypeElement.getKind().isInterface() ? Modifier.PUBLIC : Modifier.PRIVATE, Modifier.STATIC), simpleName, null, superType[0], classType, numTypeParameters[0]));
            }
        }
        // check if this may be tested above, just after assignment to a type
        if (type == null || type.getKind() == TypeKind.VOID || type.getKind() == TypeKind.OTHER || type.getKind() == TypeKind.NONE || type.getKind() == TypeKind.EXECUTABLE) {
            return result;
        }

        //currently, we cannot handle error types:
        if (Utilities.containsErrorsRecursively(type)) {
            return result;
        }

        Collection<TypeVariable> typeVars = Utilities.containedTypevarsRecursively(type);

        if (!Utilities.allTypeVarsAccessible(typeVars, target)) {
            fixTypes.remove(ElementKind.FIELD);
        }

        if (fixTypes.contains(ElementKind.FIELD) && Utilities.isTargetWritable((TypeElement) target, info)) { //IZ 111048 -- don't offer anything if target file isn't writable
            Element enclosingElement = e.getEnclosingElement();
            if (enclosingElement != null && enclosingElement.getKind() == ElementKind.ANNOTATION_TYPE) {
//                FileObject targetFile = SourceUtils.getFile(target, info.getClasspathInfo());
                FileObject targetFile = SourceUtils.getFile(ElementHandle.create(target), info.getClasspathInfo());
                if (targetFile != null) {
                    result.add(new CreateMethodFix(info, simpleName, modifiers, (TypeElement) target, type, types, Collections.<String>emptyList(), Collections.<TypeMirror>emptyList(), Collections.<String>emptyList(), targetFile));
                }

                return result;
            } else {
                FileObject targetFile = SourceUtils.getFile(ElementHandle.create(target), info.getClasspathInfo());
                if (targetFile != null) {
                    if (target.getKind() == ElementKind.ENUM) {
                        if (source != null) { //TODO: maybe create a constant? - but the test below seems very suspicious:
                        if (source.equals(target)) {
                            result.add(new CreateFieldFix(info, simpleName, modifiers, (TypeElement) target, type, targetFile));
                        } else {
                            result.add(new CreateEnumConstant(info, simpleName, modifiers, (TypeElement) target, type, targetFile));
                        }
                        }
                    } else {
                        if (firstMethod != null && info.getTrees().getElement(firstMethod).getKind() == ElementKind.CONSTRUCTOR && ErrorFixesFakeHint.isCreateFinalFieldsForCtor(ErrorFixesFakeHint.getPreferences(targetFile, FixKind.CREATE_FINAL_FIELD_CTOR))) {
                            if (CreateElementUtilities.canDeclareVariableFinal(info, firstMethod, e)) {
                                modifiers.add(Modifier.FINAL);
                            }
                        }
                        if (ErrorFixesFakeHint.enabled(info.getFileObject(), ErrorFixesFakeHint.FixKind.CREATE_FINAL_FIELD_CTOR)) {
                            result.add(new CreateFieldFix(info, simpleName, modifiers, (TypeElement) target, type, targetFile));
                        }
                    }
                }
            }
        }

        if (!wasMemberSelect && (fixTypes.contains(ElementKind.LOCAL_VARIABLE) || fixTypes.contains(ElementKind.PARAMETER) || fixTypes.contains(ElementKind.RESOURCE_VARIABLE))) {
            ExecutableElement ee = null;

            if (firstMethod != null) {
                ee = (ExecutableElement) info.getTrees().getElement(firstMethod);
            }

            int identifierPos = (int) info.getTrees().getSourcePositions().getStartPosition(info.getCompilationUnit(), errorPath.getLeaf());
            if (ee != null && fixTypes.contains(ElementKind.PARAMETER) && !Utilities.isMethodHeaderInsideGuardedBlock(info, (MethodTree) firstMethod.getLeaf()))
                result.add(new AddParameterOrLocalFix(info, type, simpleName, ElementKind.PARAMETER, identifierPos).toEditorFix());
            if ((firstMethod != null || firstInitializer != null) && fixTypes.contains(ElementKind.LOCAL_VARIABLE) && ErrorFixesFakeHint.enabled(ErrorFixesFakeHint.FixKind.CREATE_LOCAL_VARIABLE))
                result.add(new AddParameterOrLocalFix(info, type, simpleName, ElementKind.LOCAL_VARIABLE, identifierPos).toEditorFix());
            if (fixTypes.contains(ElementKind.RESOURCE_VARIABLE))
                result.add(new AddParameterOrLocalFix(info, type, simpleName, ElementKind.RESOURCE_VARIABLE, identifierPos).toEditorFix());
        }

        return result;
    }