All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.redhat.ceylon.compiler.java.codegen.AnnotationUtil Maven / Gradle / Ivy

There is a newer version: 1.3.3
Show newest version
/*
 * Copyright Red Hat Inc. and/or its affiliates and other contributors
 * as indicated by the authors tag. All rights reserved.
 *
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU General Public License version 2.
 * 
 * This particular file is subject to the "Classpath" exception as provided in the 
 * LICENSE file that accompanied this code.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT A
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License,
 * along with this distribution; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 */

package com.redhat.ceylon.compiler.java.codegen;

import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;

import static com.redhat.ceylon.model.loader.model.OutputElement.*;

import com.redhat.ceylon.common.Backend;
import com.redhat.ceylon.compiler.typechecker.analyzer.Warning;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.Annotation;
import com.redhat.ceylon.model.loader.model.AnnotationProxyClass;
import com.redhat.ceylon.model.loader.model.AnnotationProxyMethod;
import com.redhat.ceylon.model.loader.model.AnnotationTarget;
import com.redhat.ceylon.model.loader.model.OutputElement;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.Interface;
import com.redhat.ceylon.model.typechecker.model.ModelUtil;
import com.redhat.ceylon.model.typechecker.model.Type;
import com.redhat.ceylon.model.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.model.typechecker.model.Value;

/**
 *
 * @author Stéphane Épardaud 
 */
public class AnnotationUtil {

    /**
     * Returns the set of output program elements that the given annotation 
     * could be applied to. If the {@code errors} flag is true then add 
     * warnings/errors to the tree about ambigous/impossible targets.
     */
    public static EnumSet interopAnnotationTargeting(EnumSet outputs,
            Tree.Annotation annotation, boolean errors) {
        Declaration annoCtor = ((Tree.BaseMemberExpression)annotation.getPrimary()).getDeclaration();
        if (annoCtor instanceof AnnotationProxyMethod) {
            AnnotationProxyMethod proxyCtor = (AnnotationProxyMethod)annoCtor;
            AnnotationProxyClass annoClass = proxyCtor.getProxyClass();
            EnumSet possibleTargets;
            if (proxyCtor.getAnnotationTarget() != null) {
                possibleTargets = EnumSet.of(proxyCtor.getAnnotationTarget());
            } else {
                possibleTargets = AnnotationTarget.outputTargets(annoClass);
            }
            EnumSet actualTargets = possibleTargets.clone();
            actualTargets.retainAll(outputs);
            
            if (actualTargets.size() > 1) {
                if (errors) {
                    StringBuffer sb = new StringBuffer();
                    sb.append("ambiguous annotation target: ").append(annoCtor.getName());
                    sb.append(" could be applied to several targets, use one of ");
                    for (Iterator iterator = actualTargets.iterator(); iterator.hasNext();) {
                        OutputElement x = iterator.next();
                        sb.append(Naming.getDisambigAnnoCtorName((Interface)((AnnotationProxyMethod) annoCtor).getProxyClass().iface, x));
                        if (iterator.hasNext()) {
                            sb.append(", ");
                        }
                    }
                    sb.append(" to disambiguate");
                    annotation.addUsageWarning(Warning.ambiguousAnnotation, sb.toString(), Backend.Java);
                }
                return null;
            } else if (actualTargets.size() == 0) {
                if (errors) {
                    annotation.addError("no target for " + annoCtor.getName() + 
                            " annotation: @Target of @interface " + 
                            ((AnnotationProxyClass)annoClass).iface.getName() + 
                            " lists " + possibleTargets + 
                            " but annotated element tranforms to " + outputs, Backend.Java);
                }
            }
        
            return actualTargets;
        } else {
            return null;
        }
    }

    public static EnumSet outputs(Tree.ObjectDefinition that) {
        return EnumSet.of(TYPE, CONSTRUCTOR, FIELD, GETTER);
    }
    
    public static EnumSet outputs(Tree.AnyClass that) {
        EnumSet result = EnumSet.of(TYPE);
        if (!that.getDeclarationModel().hasConstructors()) {
            result.add(CONSTRUCTOR);
        }
        if (that.getDeclarationModel().isAnnotation()) {
            result.add(ANNOTATION_TYPE);
        }
        return result;
    }

    public static EnumSet outputs(Tree.PackageDescriptor annotated) {
        return EnumSet.of(TYPE);
    }

    public static EnumSet outputs(Tree.ImportModule annotated) {
        return EnumSet.of(FIELD);
    }

    public static EnumSet outputs(Tree.ModuleDescriptor annotated) {
        return EnumSet.of(TYPE);
    }
    
    public static EnumSet outputs(Tree.TypeAliasDeclaration that) {
        return EnumSet.of(TYPE);
    }
    
    public static EnumSet outputs(Tree.AnyInterface that) {
        return EnumSet.of(TYPE);
    }
    
    public static EnumSet outputs(
            Tree.Constructor annotated) {
        return EnumSet.of(CONSTRUCTOR);
    }
    
    public static EnumSet outputs(
            Tree.Enumerated annotated) {
        return EnumSet.of(CONSTRUCTOR, FIELD, GETTER);
    }
    
    public static EnumSet outputs(Tree.AnyMethod that) {
        return EnumSet.of(METHOD);
    }
    
    public static EnumSet outputs(Tree.AttributeGetterDefinition that) {
        return EnumSet.of(GETTER);
    }
    
    public static EnumSet outputs(Tree.AttributeSetterDefinition that) {
        return EnumSet.of(SETTER);
    }
    
    public static EnumSet outputs(Tree.AttributeDeclaration that) {
        EnumSet result = EnumSet.noneOf(OutputElement.class);
        Value declarationModel = that.getDeclarationModel();
        if (declarationModel != null) {
            if (declarationModel.isClassMember()) {
                if (declarationModel.isParameter()) {
                    result.add(PARAMETER);
                }
                if (declarationModel.isShared() || declarationModel.isCaptured()) {
                    result.add(GETTER);
                    if (!(that.getSpecifierOrInitializerExpression() instanceof Tree.LazySpecifierExpression)) {
                        result.add(FIELD);
                    }
                } else if (!declarationModel.isParameter()) {
                    result.add(LOCAL_VARIABLE);
                }
            } else if (declarationModel.isInterfaceMember()) {
                result.add(GETTER);
            } else if (declarationModel.isToplevel()) {
                result.add(GETTER);
                result.add(FIELD);
            } else {
                if (declarationModel.isParameter()) {
                    result.add(PARAMETER);
                } else {
                    result.add(LOCAL_VARIABLE);
                }
            }
        }
        
        if (result.contains(GETTER) 
                && (declarationModel.isVariable() || declarationModel.isLate())) {
            result.add(SETTER);
        }
        return result;
    }

    public static void duplicateInteropAnnotation(EnumSet outputs, List annotations) {
        for (int i=0; i mainTargets = interopAnnotationTargeting(outputs, ann, false);
            if (t!=null && mainTargets != null) {
                TypeDeclaration td = t.getDeclaration();
                if (!ModelUtil.isCeylonDeclaration(td)) {
                    for (int j=0; j dupeTargets = interopAnnotationTargeting(outputs, other, false);
                                if(dupeTargets != null){
                                    EnumSet sameTargets = intersection(mainTargets, dupeTargets);
                                    if(!sameTargets.isEmpty()){
                                        ann.addError("duplicate annotation: there are multiple annotations of type '" + 
                                                td.getName() + "' for targets: '"+sameTargets+"'");
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    private static EnumSet intersection(EnumSet mainTargets, EnumSet dupeTargets) {
        EnumSet intersect = EnumSet.copyOf(mainTargets);
        intersect.retainAll(dupeTargets);
        return intersect;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy