<commit_before>/*
 * Copyright 2008 <NAME>.
 *
 * 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.qi4j.runtime.query.grammar.impl;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import org.qi4j.api.entity.EntityComposite;
import org.qi4j.api.entity.association.Association;
import org.qi4j.api.query.QueryExpressionException;
import org.qi4j.api.query.grammar.AssociationReference;
import org.qi4j.runtime.entity.EntityInstance;

/**
 * Default {@link AssociationReference}.
 */
public class AssociationReferenceImpl
    implements AssociationReference
{

    /**
     * Association name.
     */
    private final String name;
    /**
     * Interface that declared the association.
     */
    private final Class<?> declaringType;
    /**
     * Association accessor method.
     */
    private final Method accessor;

    /**
     * Association type.
     */
    private final Type type;

    /**
     * Traversed association.
     */
    private final AssociationReference traversed;

    /**
     * Constructor.
     *
     * @param accessor  method that acts as association
     * @param traversed traversed association
     */
    public AssociationReferenceImpl( final Method accessor,
                                     final AssociationReference traversed
    )
    {
        this.accessor = accessor;
        name = accessor.getName();
        declaringType = accessor.getDeclaringClass();
        Type returnType = accessor.getGenericReturnType();
        if( !( returnType instanceof ParameterizedType ) )
        {
            throw new QueryExpressionException( "Unsupported association type:" + returnType );
        }
        Type associationTypeAsType = ( (ParameterizedType) returnType ).getActualTypeArguments()[ 0 ];
        if( !( associationTypeAsType instanceof Class ) )
        {
            throw new QueryExpressionException( "Unsupported association type:" + associationTypeAsType );
        }
        type = associationTypeAsType;
        this.traversed = traversed;
    }

    /**
     * @see AssociationReference#associationName()
     */
    public String associationName()
    {
        return name;
    }

    /**
     * @see AssociationReference#associationDeclaringType()
     */
    public Class<?> associationDeclaringType()
    {
        return declaringType;
    }

    /**
     * @see AssociationReference#associationAccessor()
     */
    public Method associationAccessor()
    {
        return accessor;
    }

    /**
     * @see AssociationReference#associationType()
     */
    public Type associationType()
    {
        return type;
    }

    /**
     * @see AssociationReference#traversedAssociation()
     */
    public AssociationReference traversedAssociation()
    {
        return traversed;
    }

    /**
     * @see AssociationReference#eval(Object)
     */
    public Object eval( final Object target )
    {
        Object actual = target;
        if( traversedAssociation() != null )
        {
            actual = traversedAssociation().eval( target );
        }
        if( actual != null )
        {
            try
            {
                Association assoc = (Association) EntityInstance.getEntityInstance( (EntityComposite) actual ).invokeProxy( associationAccessor(), new Object[0] );
                return assoc.get();
            }
            catch( Throwable e )
            {
                return null;
            }
        }
        return null;
    }

    @Override
    public String toString()
    {
        StringBuilder fragment = new StringBuilder();
        if( traversed != null )
        {
            fragment.append( traversed.toString() ).append( "." );
        }
        fragment.append( declaringType.getSimpleName() );
        fragment.append( ":" );
        fragment.append( name );
        return fragment.toString();
    }<commit_msg>Similar fix as for subtyped property references<commit_after>/*
 * Copyright 2008 <NAME>.
 *
 * 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.qi4j.runtime.query.grammar.impl;

import org.qi4j.api.entity.EntityComposite;
import org.qi4j.api.entity.association.Association;
import org.qi4j.api.entity.association.GenericAssociationInfo;
import org.qi4j.api.query.QueryExpressionException;
import org.qi4j.api.query.grammar.AssociationReference;
import org.qi4j.api.util.Classes;
import org.qi4j.runtime.entity.EntityInstance;

import java.lang.reflect.Method;
import java.lang.reflect.Type;

/**
 * Default {@link AssociationReference}.
 */
public class AssociationReferenceImpl
        implements AssociationReference
{

    /**
     * Association name.
     */
    private final String name;
    /**
     * Interface that declared the association.
     */
    private final Class<?> declaringType;
    /**
     * Association accessor method.
     */
    private final Method accessor;

    /**
     * Association type.
     */
    private final Type type;

    /**
     * Traversed association.
     */
    private final AssociationReference traversed;

    /**
     * Constructor.
     *
     * @param accessor  method that acts as association
     * @param traversed traversed association
     */
    public AssociationReferenceImpl( final Method accessor,
                                     final AssociationReference traversed
    )
    {
        this.accessor = accessor;
        name = accessor.getName();
        declaringType = accessor.getDeclaringClass();
        Type returnType = accessor.getGenericReturnType();
        if (Association.class.isAssignableFrom( Classes.getRawClass( returnType ) ))
        {
            throw new QueryExpressionException( "Unsupported association type:" + returnType );
        }
        Type associationTypeAsType = GenericAssociationInfo.getAssociationType( returnType );
        if (!(associationTypeAsType instanceof Class))
        {
            throw new QueryExpressionException( "Unsupported association type:" + associationTypeAsType );
        }
        type = associationTypeAsType;
        this.traversed = traversed;
    }

    /**
     * @see AssociationReference#associationName()
     */
    public String associationName()
    {
        return name;
    }

    /**
     * @see AssociationReference#associationDeclaringType()
     */
    public Class<?> associationDeclaringType()
    {
        return declaringType;
    }

    /**
     * @see AssociationReference#associationAccessor()
     */
    public Method associationAccessor()
    {
        return accessor;
    }

    /**
     * @see AssociationReference#associationType()
     */
    public Type associationType()
    {
        return type;
    }

    /**
     * @see AssociationReference#traversedAssociation()
     */
    public AssociationReference traversedAssociation()
    {
        return traversed;
    }

    /**
     * @see AssociationReference#eval(Object)
     */
    public Object eval( final Object target )
    {
        Object actual = target;
        if (traversedAssociation() != null)
        {
            actual = traversedAssociation().eval( target );
        }
        if (actual != null)
        {
            try
            {
                Association assoc = (Association) EntityInstance.getEntityInstance( (EntityComposite) actual ).invokeProxy( associationAccessor(), new Object[0] );
                return assoc.get();
            }
            catch (Throwable e)
            {
                return null;
            }
        }
        return null;
    }

    @Override
    public String toString()
    {
        StringBuilder fragment = new StringBuilder();
        if (traversed != null)
        {
            fragment.append( traversed.toString() ).append( "." );
        }
        fragment.append( declaringType.getSimpleName() );
        fragment.append( ":" );
        fragment.append( name );
        return fragment.toString();
    }
{'fraction_non_alphanumeric': 0.09479678427869585, 'fraction_numerical': 0.0036846806610093793, 'mean_word_length': 2.297864506627393, 'pattern_counts': {'":': 2, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3232814', 'n_tokens_mistral': 2317, 'n_tokens_neox': 2238, 'n_words': 604}