<commit_before>/*
 * Copyright 2016 Layne Mobile, LLC
 *
 * 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.laynemobile.proxy.model;

import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.laynemobile.proxy.Util;
import com.laynemobile.proxy.annotations.Generate;
import com.laynemobile.proxy.functions.Func0;
import com.squareup.javapoet.ClassName;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;

import sourcerer.processor.Env;

public final class ProxyElement implements Comparable<ProxyElement> {
    private static final Map<TypeElement, ProxyElement> CACHE = new HashMap<>();

    private final TypeElement element;
    private final ClassName className;
    private final Metadata metadata;
    private final ImmutableList<TypeVariable> typeVariables;
    private final ImmutableList<ProxyType> interfaceTypes;
    private final ImmutableList<FunctionElement> functions;

    private ProxyElement(TypeElement element, Metadata metadata, List<TypeVariable> typeVariables,
            List<ProxyType> interfaceTypes, List<FunctionElement> functions) {
        this.element = element;
        this.className = ClassName.get(element);
        this.metadata = metadata;
        this.typeVariables = ImmutableList.copyOf(typeVariables);
        this.interfaceTypes = ImmutableList.copyOf(interfaceTypes);
        this.functions = ImmutableList.copyOf(functions);
    }

    public static ProxyElement parse(Element element, Env env) {
        // Ensure it is an interface element
        if (element.getKind() != ElementKind.INTERFACE) {
            env.error(element, "Only interfaces can be annotated with @%s",
                    Generate.ProxyBuilder.class.getSimpleName());
            return null; // Exit processing
        }
        TypeElement typeElement = (TypeElement) element;
        synchronized (CACHE) {
            ProxyElement proxyElement = CACHE.get(typeElement);
            if (proxyElement != null) {
                env.log("returning cached proxy element: %s", proxyElement);
                return proxyElement;
            }
        }
        ProxyElement proxyElement = parse(typeElement, env);
        env.log("caching proxy element: %s", proxyElement);
        synchronized (CACHE) {
            CACHE.put(typeElement, proxyElement);
        }
        return proxyElement;
    }

    private static ProxyElement parse(TypeElement element, Env env) {
        List<? extends TypeMirror> interfaces = element.getInterfaces();
        List<? extends TypeParameterElement> typeParameters = element.getTypeParameters();

        List<TypeVariable> typeVariables = new ArrayList<>(typeParameters.size());
        env.log("typeParameters: %s", typeParameters);
        for (TypeParameterElement typeParameter : typeParameters) {
            env.log("typeParameter: %s", typeParameter);
            env.log("typeParameter bounds: %s", typeParameter.getBounds());
            ElementKind paramKind = typeParameter.getKind();
            env.log("typeParameter kind: %s", paramKind);
            TypeMirror paramType = typeParameter.asType();
            env.log("typeParameter type: %s", paramType);
            if (paramType.getKind() == TypeKind.TYPEVAR) {
                typeVariables.add((TypeVariable) paramType);
            }
        }

        List<ProxyType> interfaceTypes = new ArrayList<>(interfaces.size());
        for (TypeMirror interfaceType : interfaces) {
            ProxyType elementType = ProxyType.parse(interfaceType, env);
            if (elementType != null) {
                env.log("interface type: %s", interfaceType);
                interfaceTypes.add(elementType);
            }
        }

        List<? extends Element> enclosedElements = element.getEnclosedElements();
        List<FunctionElement> functions = new ArrayList<>(enclosedElements.size());
        for (Element enclosed : enclosedElements) {
            FunctionElement functionElement = FunctionElement.parse(enclosed, env);
            if (functionElement == null) {
                continue;
            }
            functions.add(functionElement);
        }

        Metadata metadata = Metadata.parse(element, env);
        return new ProxyElement(element, metadata, typeVariables, interfaceTypes, functions);
    }

    public TypeElement element() {
        return element;
    }

    public ClassName className() {
        return className;
    }

    public String packageName() {
        return className.packageName();
    }

    public Metadata metadata() {
        return metadata;
    }

    public ImmutableList<TypeVariable> typeVariables() {
        return typeVariables;
    }

    public ImmutableList<ProxyType> interfaceTypes() {
        return interfaceTypes;
    }

    public ImmutableList<FunctionElement> functions() {
        return functions;
    }

    @Override public int compareTo(ProxyElement o) {
        if (equals(o) || element.equals(o.element)) {
            System.out.printf("\n\n'%s'\nequals\n'%s'\n\n", this, o);
            return 0;
        } else if (o.dependsOn(this)) {
            System.out.printf("\n\n'%s'\ndependsOn\n'%s'\n\n", o, this);
            return -1;
        } else if (dependsOn(o)) {
            System.out.printf("\n\n'%s'\ndependsOn\n'%s'\n\n", this, o);
            return 1;
        }
        System.out.printf("\n\n'%s'\nequals-compareName\n'%s'\n\n", this, o);
        return element.getQualifiedName().toString()
                .compareTo(o.element.getQualifiedName().toString());
    }

    private boolean dependsOn(ProxyElement o) {
        TypeElement element = o.element;
        return metadata.dependsOn.contains(element)
                || metadata.replaces.equals(element)
                || metadata.extendsFrom.equals(element)
                || o.isInList(interfaceTypes)
                || o.metadata.parent && !metadata.parent;
    }

    private boolean isInList(List<ProxyType> proxyTypes) {<commit_msg>parentheticals<commit_after>/*
 * Copyright 2016 Layne Mobile, LLC
 *
 * 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.laynemobile.proxy.model;

import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.laynemobile.proxy.Util;
import com.laynemobile.proxy.annotations.Generate;
import com.laynemobile.proxy.functions.Func0;
import com.squareup.javapoet.ClassName;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;

import sourcerer.processor.Env;

public final class ProxyElement implements Comparable<ProxyElement> {
    private static final Map<TypeElement, ProxyElement> CACHE = new HashMap<>();

    private final TypeElement element;
    private final ClassName className;
    private final Metadata metadata;
    private final ImmutableList<TypeVariable> typeVariables;
    private final ImmutableList<ProxyType> interfaceTypes;
    private final ImmutableList<FunctionElement> functions;

    private ProxyElement(TypeElement element, Metadata metadata, List<TypeVariable> typeVariables,
            List<ProxyType> interfaceTypes, List<FunctionElement> functions) {
        this.element = element;
        this.className = ClassName.get(element);
        this.metadata = metadata;
        this.typeVariables = ImmutableList.copyOf(typeVariables);
        this.interfaceTypes = ImmutableList.copyOf(interfaceTypes);
        this.functions = ImmutableList.copyOf(functions);
    }

    public static ProxyElement parse(Element element, Env env) {
        // Ensure it is an interface element
        if (element.getKind() != ElementKind.INTERFACE) {
            env.error(element, "Only interfaces can be annotated with @%s",
                    Generate.ProxyBuilder.class.getSimpleName());
            return null; // Exit processing
        }
        TypeElement typeElement = (TypeElement) element;
        synchronized (CACHE) {
            ProxyElement proxyElement = CACHE.get(typeElement);
            if (proxyElement != null) {
                env.log("returning cached proxy element: %s", proxyElement);
                return proxyElement;
            }
        }
        ProxyElement proxyElement = parse(typeElement, env);
        env.log("caching proxy element: %s", proxyElement);
        synchronized (CACHE) {
            CACHE.put(typeElement, proxyElement);
        }
        return proxyElement;
    }

    private static ProxyElement parse(TypeElement element, Env env) {
        List<? extends TypeMirror> interfaces = element.getInterfaces();
        List<? extends TypeParameterElement> typeParameters = element.getTypeParameters();

        List<TypeVariable> typeVariables = new ArrayList<>(typeParameters.size());
        env.log("typeParameters: %s", typeParameters);
        for (TypeParameterElement typeParameter : typeParameters) {
            env.log("typeParameter: %s", typeParameter);
            env.log("typeParameter bounds: %s", typeParameter.getBounds());
            ElementKind paramKind = typeParameter.getKind();
            env.log("typeParameter kind: %s", paramKind);
            TypeMirror paramType = typeParameter.asType();
            env.log("typeParameter type: %s", paramType);
            if (paramType.getKind() == TypeKind.TYPEVAR) {
                typeVariables.add((TypeVariable) paramType);
            }
        }

        List<ProxyType> interfaceTypes = new ArrayList<>(interfaces.size());
        for (TypeMirror interfaceType : interfaces) {
            ProxyType elementType = ProxyType.parse(interfaceType, env);
            if (elementType != null) {
                env.log("interface type: %s", interfaceType);
                interfaceTypes.add(elementType);
            }
        }

        List<? extends Element> enclosedElements = element.getEnclosedElements();
        List<FunctionElement> functions = new ArrayList<>(enclosedElements.size());
        for (Element enclosed : enclosedElements) {
            FunctionElement functionElement = FunctionElement.parse(enclosed, env);
            if (functionElement == null) {
                continue;
            }
            functions.add(functionElement);
        }

        Metadata metadata = Metadata.parse(element, env);
        return new ProxyElement(element, metadata, typeVariables, interfaceTypes, functions);
    }

    public TypeElement element() {
        return element;
    }

    public ClassName className() {
        return className;
    }

    public String packageName() {
        return className.packageName();
    }

    public Metadata metadata() {
        return metadata;
    }

    public ImmutableList<TypeVariable> typeVariables() {
        return typeVariables;
    }

    public ImmutableList<ProxyType> interfaceTypes() {
        return interfaceTypes;
    }

    public ImmutableList<FunctionElement> functions() {
        return functions;
    }

    @Override public int compareTo(ProxyElement o) {
        if (equals(o) || element.equals(o.element)) {
            System.out.printf("\n\n'%s'\nequals\n'%s'\n\n", this, o);
            return 0;
        } else if (o.dependsOn(this)) {
            System.out.printf("\n\n'%s'\ndependsOn\n'%s'\n\n", o, this);
            return -1;
        } else if (dependsOn(o)) {
            System.out.printf("\n\n'%s'\ndependsOn\n'%s'\n\n", this, o);
            return 1;
        }
        System.out.printf("\n\n'%s'\nequals-compareName\n'%s'\n\n", this, o);
        return element.getQualifiedName().toString()
                .compareTo(o.element.getQualifiedName().toString());
    }

    private boolean dependsOn(ProxyElement o) {
        TypeElement element = o.element;
        return metadata.dependsOn.contains(element)
                || metadata.replaces.equals(element)
                || metadata.extendsFrom.equals(element)
                || o.isInList(interfaceTypes)
                || (o.metadata.parent && !metadata.parent);
    }

    private boolean isInList(List<ProxyType> proxyTypes) {
{'fraction_non_alphanumeric': 0.10413187132379638, 'fraction_numerical': 0.0017427928255028683, 'mean_word_length': 2.807575338678463, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 47, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, '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': '6442407', 'n_tokens_mistral': 3652, 'n_tokens_neox': 3555, 'n_words': 888}