<commit_before>package org.xcordion.ide.intellij;

import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.html.HtmlFileImpl;
import static com.intellij.psi.search.GlobalSearchScope.allScope;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import org.apache.commons.lang.StringUtils;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

class XcordionReflectionUtils {
    private static final List<String> EXCLUDED_METHODS = Arrays.asList(
            "clone",
            "finalize",
            "Object",
            "registerNatives"
    );
    private static final Pattern LAST_DOT_PATTERN = Pattern.compile("^(.*)\\.\\s*$", Pattern.DOTALL);
    private static final Pattern LEFT_HAND_EXPRESSION_PATTERN = Pattern.compile("^(.*)\\b(\\w+)(\\(" + parenInnards(6) + "\\))?(\\[" + bracketInnards(6) + "\\])?\\s*$", Pattern.DOTALL);
    private static final Pattern VARIABLE_NAME_PATTERN = Pattern.compile("(#\\w+)");
    private static final Pattern GETTER_SETTER_PATTERN = Pattern.compile("^(is|[gs]et)([A-Z])(\\w+)$");
    private static final String ENDS_WITH_HASH = "^(.*)\\n(\\s*.*)*#";

    private XcordionReflectionUtils() {
        // static class
    }

    static List<AutoCompleteItem> getAutoCompleteItems(XmlAttributeValue attributeValueElement, String suffix, String baseExpression) {
        List<AutoCompleteItem> displayValues = new ArrayList<AutoCompleteItem>();
        if (!baseExpression.endsWith("#")) {
            displayValues.addAll(getMethodAndFieldNameVariants(attributeValueElement, baseExpression, suffix));
        }

//        if (StringUtils.isBlank(baseExpression.trim()) || baseExpression.trim().matches(ENDS_WITH_HASH) || baseExpression.trim().endsWith(",")) {
//            displayValues.addAll(getVariableNameVariants(attributeValueElement, baseExpression, suffix));
//        }

        return displayValues;
    }

    private static Collection<AutoCompleteItem> getMethodAndFieldNameVariants(XmlAttributeValue attributeValueElement, String baseExpression, String suffix) {
        List<AutoCompleteItem> displayValues = new ArrayList<AutoCompleteItem>();
        PsiClass clazz = findMember(baseExpression, attributeValueElement);

        if (clazz != null) {
            displayValues.addAll(getMethodsToDisplay(clazz, suffix));
            displayValues.addAll(getFieldsToDisplay(clazz, suffix));
        }
        return displayValues;
    }

    private static List<AutoCompleteItem> getFieldsToDisplay(PsiClass clazz, String suffix) {
        List<AutoCompleteItem> fields = new ArrayList<AutoCompleteItem>();

        for (PsiField field : clazz.getAllFields()) {
            if (isPublic(field)) {
                String expression = ifMatchesSuffix(suffix, field.getName());
                if (expression != null) {
                    fields.add(new AutoCompleteItem(expression, field.getType().getPresentableText()));
                }
            }
        }
        return fields;
    }

    private static List<AutoCompleteItem> getMethodsToDisplay(PsiClass clazz, String suffix) {
        List<AutoCompleteItem> methods = new ArrayList<AutoCompleteItem>();

        for (PsiMethod method : clazz.getAllMethods()) {
            if (isPotentialMethod(method)) {
                Matcher m = GETTER_SETTER_PATTERN.matcher(method.getName());
                String expression = null;
                if (m.matches() && (isGetter(method, m) || isBooleanGetter(method, m) || isSetter(method, m))) {
                    // OGNL getter or setter
                    expression = ifMatchesSuffix(suffix, m.group(2).toLowerCase() + m.group(3));
                }
                if (expression == null) {
                    // normal method
                    expression = ifMatchesSuffix(suffix, method.getName() + getParameterString(method));
                }
                if (expression != null) {
                    methods.add(new AutoCompleteItem(expression, method.getReturnType().getPresentableText()));
                }
            }
        }
        return methods;
    }

    private static String getParameterString(PsiMethod method) {
        String parameterString = "(";
        PsiParameter[] parameters = method.getParameterList().getParameters();
        for (int i = 0; i < parameters.length; i++) {
            PsiParameter parameter = parameters[i];
            parameterString += parameter.getType().getPresentableText();
            if (i != parameters.length - 1) {
                parameterString += ", ";
            }
        }
        parameterString += ")";
        return parameterString;
    }

    private static boolean isPotentialMethod(PsiMethod method) {
        return !method.isConstructor()
                && !EXCLUDED_METHODS.contains(method.getName())
                && isPublic(method);
    }

    private static boolean isSetter(PsiMethod method, Matcher m) {
        return m.group(1).equals("set") && method.getParameterList().getParametersCount() == 1;
    }

    private static boolean isBooleanGetter(PsiMethod method, Matcher m) {
        return m.group(1).equals("is") && method.getParameterList().getParametersCount() == 0
                && method.getReturnType().isConvertibleFrom(PsiType.BOOLEAN);
    }

    private static boolean isGetter(PsiMethod method, Matcher m) {
        return m.group(1).equals("get") && method.getParameterList().getParametersCount() == 0;
    }

    private static String ifMatchesSuffix(String suffix, String expression) {
        return (suffix == null || expression.toLowerCase().startsWith(suffix.toLowerCase())) ? expression : null;
    }

    private static boolean isPublic(PsiModifierListOwner modifiable) {
        return modifiable.getModifierList().hasExplicitModifier("public");
    }

    private static List<AutoCompleteItem> getVariableNameVariants(PsiElement attributeValueElement, String baseExpression, String suffix) {
        XmlFile doc = (XmlFile) attributeValueElement.getContainingFile();
        TreeSet<String> ognlVariableNames = new TreeSet<String>();
        recursivelyScanXcordionTags(ognlVariableNames, doc, attributeValueElement);
        ognlVariableNames.add("#VALUE");
        ognlVariableNames.add("#HREF");

        String prefix = baseExpression;
        if (baseExpression.endsWith("#")) {
            prefix = baseExpression.substring(0, baseExpression.length() - 1);
            suffix = "#" + (suffix == null ? "" : suffix);
        }

        List<AutoCompleteItem> displayValues = new ArrayList<AutoCompleteItem>();
        for (String variable : ognlVariableNames) {
            if (suffix == null || variable.startsWith(suffix)) {
                if (prefix.length() == 0 && suffix != null && suffix.startsWith("#")) {
                    variable = variable.substring(1);
                }
                displayValues.add(new AutoCompleteItem(variable, "html variable"));
            }
        }
        return displayValues;
    }

    private static void recursivelyScanXcordionTags(Set<String> ognlVariableNames, PsiElement element, PsiElement attributeValueElement) {
        for (PsiElement psiChild : element.getChildren()) {
            if (psiChild instanceof XmlTag) {
                XmlTag tag = (XmlTag) psiChild;
                for (XmlAttribute attribute : tag.getAttributes()) {
                    if (XcordionAttribute.isXcordionAttribute(attribute) && attribute.getValueElement() != attributeValueElement) {
                        Matcher matcher = VARIABLE_NAME_PATTERN.matcher(attribute.getValue());
                        while (matcher.find()) {
                            ognlVariableNames.add(matcher.group(1));
                        }
                    }
                }
            }
            recursivelyScanXcordionTags(ognlVariableNames, psiChild, attributeValueElement);
        }
    }

    private static PsiClass getXcordionTestBackingClass(PsiElement psiElement) {
        if (psiElement instanceof XmlAttributeValue) {
            HtmlFileImpl htmlFile = getHtmlFile(psiElement);
            String qualifiedPackageName = getQualifiedPackageName(htmlFile);

            PsiClass psiClass = findTestClass(psiElement, htmlFile, qualifiedPackageName, "Test");
            if (psiClass == null) {
                psiClass = findTestClass(psiElement, htmlFile, qualifiedPackageName, "");
            }
            return psiClass;
        }<commit_msg>Brad and MikeD: trying to stop auto complete from locking up :S<commit_after>package org.xcordion.ide.intellij;

import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.html.HtmlFileImpl;
import static com.intellij.psi.search.GlobalSearchScope.allScope;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import org.apache.commons.lang.StringUtils;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

class XcordionReflectionUtils {
    private static final List<String> EXCLUDED_METHODS = Arrays.asList(
            "clone",
            "finalize",
            "Object",
            "registerNatives"
    );
    private static final Pattern LAST_DOT_PATTERN = Pattern.compile("^(.*)\\.\\s*$", Pattern.DOTALL);
    private static final Pattern LEFT_HAND_EXPRESSION_PATTERN = Pattern.compile("^(.*)\\b(\\w+)(\\(" + parenInnards(6) + "\\))?(\\[" + bracketInnards(6) + "\\])?\\s*$", Pattern.DOTALL);
    private static final Pattern VARIABLE_NAME_PATTERN = Pattern.compile("(#\\w+)");
    private static final Pattern GETTER_SETTER_PATTERN = Pattern.compile("^(is|[gs]et)([A-Z])(\\w+)$");
    private static final String ENDS_WITH_HASH = "^(.*)\\n(\\s*.*)*#";

    private XcordionReflectionUtils() {
        // static class
    }

    static List<AutoCompleteItem> getAutoCompleteItems(XmlAttributeValue attributeValueElement, String suffix, String baseExpression) {
        List<AutoCompleteItem> displayValues = new ArrayList<AutoCompleteItem>();
        if (!baseExpression.endsWith("#")) {
            displayValues.addAll(getMethodAndFieldNameVariants(attributeValueElement, baseExpression, suffix));
        }
//
//        if (StringUtils.isBlank(baseExpression.trim()) || baseExpression.trim().matches(ENDS_WITH_HASH) || baseExpression.trim().endsWith(",")) {
//            displayValues.addAll(getVariableNameVariants(attributeValueElement, baseExpression, suffix));
//        }

        return displayValues;
    }

    private static Collection<AutoCompleteItem> getMethodAndFieldNameVariants(XmlAttributeValue attributeValueElement, String baseExpression, String suffix) {
        List<AutoCompleteItem> displayValues = new ArrayList<AutoCompleteItem>();
        PsiClass clazz = findMember(baseExpression, attributeValueElement);

        if (clazz != null) {
            displayValues.addAll(getMethodsToDisplay(clazz, suffix));
            displayValues.addAll(getFieldsToDisplay(clazz, suffix));
        }
        return displayValues;
    }

    private static List<AutoCompleteItem> getFieldsToDisplay(PsiClass clazz, String suffix) {
        List<AutoCompleteItem> fields = new ArrayList<AutoCompleteItem>();

        for (PsiField field : clazz.getAllFields()) {
            if (isPublic(field)) {
                String expression = ifMatchesSuffix(suffix, field.getName());
                if (expression != null) {
                    fields.add(new AutoCompleteItem(expression, field.getType().getPresentableText()));
                }
            }
        }
        return fields;
    }

    private static List<AutoCompleteItem> getMethodsToDisplay(PsiClass clazz, String suffix) {
        List<AutoCompleteItem> methods = new ArrayList<AutoCompleteItem>();

        for (PsiMethod method : clazz.getAllMethods()) {
            if (isPotentialMethod(method)) {
                Matcher m = GETTER_SETTER_PATTERN.matcher(method.getName());
                String expression = null;
                if (m.matches() && (isGetter(method, m) || isBooleanGetter(method, m) || isSetter(method, m))) {
                    // OGNL getter or setter
                    expression = ifMatchesSuffix(suffix, m.group(2).toLowerCase() + m.group(3));
                }
                if (expression == null) {
                    // normal method
                    expression = ifMatchesSuffix(suffix, method.getName() + getParameterString(method));
                }
                if (expression != null) {
                    methods.add(new AutoCompleteItem(expression, method.getReturnType().getPresentableText()));
                }
            }
        }
        return methods;
    }

    private static String getParameterString(PsiMethod method) {
        String parameterString = "(";
        PsiParameter[] parameters = method.getParameterList().getParameters();
        for (int i = 0; i < parameters.length; i++) {
            PsiParameter parameter = parameters[i];
            parameterString += parameter.getType().getPresentableText();
            if (i != parameters.length - 1) {
                parameterString += ", ";
            }
        }
        parameterString += ")";
        return parameterString;
    }

    private static boolean isPotentialMethod(PsiMethod method) {
        return !method.isConstructor()
                && !EXCLUDED_METHODS.contains(method.getName())
                && isPublic(method);
    }

    private static boolean isSetter(PsiMethod method, Matcher m) {
        return m.group(1).equals("set") && method.getParameterList().getParametersCount() == 1;
    }

    private static boolean isBooleanGetter(PsiMethod method, Matcher m) {
        return m.group(1).equals("is") && method.getParameterList().getParametersCount() == 0
                && method.getReturnType().isConvertibleFrom(PsiType.BOOLEAN);
    }

    private static boolean isGetter(PsiMethod method, Matcher m) {
        return m.group(1).equals("get") && method.getParameterList().getParametersCount() == 0;
    }

    private static String ifMatchesSuffix(String suffix, String expression) {
        return (suffix == null || expression.toLowerCase().startsWith(suffix.toLowerCase())) ? expression : null;
    }

    private static boolean isPublic(PsiModifierListOwner modifiable) {
        return modifiable.getModifierList().hasExplicitModifier("public");
    }

    private static List<AutoCompleteItem> getVariableNameVariants(PsiElement attributeValueElement, String baseExpression, String suffix) {
        XmlFile doc = (XmlFile) attributeValueElement.getContainingFile();
        TreeSet<String> ognlVariableNames = new TreeSet<String>();
        recursivelyScanXcordionTags(ognlVariableNames, doc, attributeValueElement, new ArrayList<PsiElement>());
        ognlVariableNames.add("#VALUE");
        ognlVariableNames.add("#HREF");

        String prefix = baseExpression;
        if (baseExpression.endsWith("#")) {
            prefix = baseExpression.substring(0, baseExpression.length() - 1);
            suffix = "#" + (suffix == null ? "" : suffix);
        }

        List<AutoCompleteItem> displayValues = new ArrayList<AutoCompleteItem>();
        for (String variable : ognlVariableNames) {
            if (suffix == null || variable.startsWith(suffix)) {
                if (prefix.length() == 0 && suffix != null && suffix.startsWith("#")) {
                    variable = variable.substring(1);
                }
                displayValues.add(new AutoCompleteItem(variable, "html variable"));
            }
        }
        return displayValues;
    }

    private static void recursivelyScanXcordionTags(Set<String> ognlVariableNames, PsiElement element, PsiElement attributeValueElement, List<PsiElement> elementsAlreadyScanned) {
        elementsAlreadyScanned.add(element);
        for (PsiElement psiChild : element.getChildren()) {
            if (psiChild instanceof XmlTag) {
                XmlTag tag = (XmlTag) psiChild;
                for (XmlAttribute attribute : tag.getAttributes()) {
                    if (XcordionAttribute.isXcordionAttribute(attribute) && attribute.getValueElement() != attributeValueElement) {
                        Matcher matcher = VARIABLE_NAME_PATTERN.matcher(attribute.getValue());
                        while (matcher.find()) {
                            ognlVariableNames.add(matcher.group(1));
                        }
                    }
                }
            }
            if(!elementsAlreadyScanned.contains(psiChild)){
                recursivelyScanXcordionTags(ognlVariableNames, psiChild, attributeValueElement, elementsAlreadyScanned);
            }
        }
    }

    private static PsiClass getXcordionTestBackingClass(PsiElement psiElement) {
        if (psiElement instanceof XmlAttributeValue) {
            HtmlFileImpl htmlFile = getHtmlFile(psiElement);
            String qualifiedPackageName = getQualifiedPackageName(htmlFile);

            PsiClass psiClass = findTestClass(psiElement, htmlFile, qualifiedPackageName, "Test");
            if (psiClass == null) {
                psiClass = findTestClass(psiElement, htmlFile, qualifiedPackageName, "");
            }
            return psiClass;
        }
{'fraction_non_alphanumeric': 0.11336659212110786, 'fraction_numerical': 0.0019496530764378692, 'mean_word_length': 2.7043330501274427, 'pattern_counts': {'":': 0, '<': 45, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 8}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4631760', 'n_tokens_mistral': 4675, 'n_tokens_neox': 4492, 'n_words': 892}