001package ball.game.scrabble;
002/*-
003 * ##########################################################################
004 * Game Applications and Utilities
005 * $Id: PremiumProcessor.html 5431 2020-02-12 19:03:17Z ball $
006 * $HeadURL: svn+ssh://svn.hcf.dev/var/spool/scm/repository.svn/hcf-dev/blog/2019-07-08-fivethirtyeight-best-scrabble-string/src/main/resources/javadoc/src-html/ball/game/scrabble/PremiumProcessor.html $
007 * %%
008 * Copyright (C) 2010 - 2020 Allen D. Ball
009 * %%
010 * Licensed under the Apache License, Version 2.0 (the "License");
011 * you may not use this file except in compliance with the License.
012 * You may obtain a copy of the License at
013 *
014 *      http://www.apache.org/licenses/LICENSE-2.0
015 *
016 * Unless required by applicable law or agreed to in writing, software
017 * distributed under the License is distributed on an "AS IS" BASIS,
018 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
019 * See the License for the specific language governing permissions and
020 * limitations under the License.
021 * ##########################################################################
022 */
023import ball.annotation.ServiceProviderFor;
024import ball.annotation.processing.AbstractAnnotationProcessor;
025import ball.annotation.processing.For;
026import java.lang.annotation.Annotation;
027import java.util.HashSet;
028import java.util.List;
029import javax.annotation.processing.ProcessingEnvironment;
030import javax.annotation.processing.Processor;
031import javax.annotation.processing.RoundEnvironment;
032import javax.lang.model.element.Element;
033import javax.lang.model.element.TypeElement;
034import lombok.NoArgsConstructor;
035import lombok.ToString;
036
037import static javax.lang.model.element.Modifier.ABSTRACT;
038import static javax.lang.model.element.Modifier.PUBLIC;
039import static javax.tools.Diagnostic.Kind.ERROR;
040
041/**
042 * {@link Processor} implementation to check {@link Class}es annotated with
043 * {@link LetterPremium} or {@link WordPremium}:
044 * <ol>
045 *   <li value="1">Are an instance of {@link SQ},</li>
046 *   <li value="2">Concrete, and</li>
047 *   <li value="3">
048 *      Are annotated with only one of {@link LetterPremium} or
049 *      {@link WordPremium}
050 *   </li>
051 * </ol>
052 *
053 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball}
054 * @version $Revision: 5431 $
055 */
056@ServiceProviderFor({ Processor.class })
057@For({ LetterPremium.class, WordPremium.class })
058@NoArgsConstructor @ToString
059public class PremiumProcessor extends AbstractAnnotationProcessor {
060    private TypeElement supertype = null;
061
062    @Override
063    public void init(ProcessingEnvironment processingEnv) {
064        super.init(processingEnv);
065
066        try {
067            supertype = getTypeElementFor(SQ.class);
068        } catch (Exception exception) {
069            print(ERROR, null, exception);
070        }
071    }
072
073    @Override
074    public void process(RoundEnvironment roundEnv,
075                        TypeElement annotation,
076                        Element element) throws Exception {
077        switch (element.getKind()) {
078        case CLASS:
079            if (isAssignable(element.asType(), supertype.asType())) {
080                if (! element.getModifiers().contains(ABSTRACT)) {
081                    if (hasPublicNoArgumentConstructor(element)) {
082                        HashSet<TypeElement> set = new HashSet<>();
083
084                        for (Class<? extends Annotation> type :
085                                 getSupportedAnnotationTypeList()) {
086                            if (element.getAnnotation(type) != null) {
087                                set.add(getTypeElementFor(type));
088                            }
089                        }
090
091                        set.remove(annotation);
092
093                        if (! set.isEmpty()) {
094                            print(ERROR,
095                                  element,
096                                  element.getKind() + " annotated with "
097                                  + AT + annotation.getSimpleName()
098                                  + " but is also annotated with "
099                                  + AT + set.iterator().next().getSimpleName());
100                        }
101                    } else {
102                        print(ERROR,
103                              element,
104                              element.getKind() + " annotated with "
105                              + AT + annotation.getSimpleName()
106                              + " but does not have a " + PUBLIC
107                              + " no-argument constructor");
108                    }
109                } else {
110                    print(ERROR,
111                          element,
112                          element.getKind() + " annotated with "
113                          + AT + annotation.getSimpleName()
114                          + " but is " + ABSTRACT);
115                }
116            } else {
117                print(ERROR,
118                      element,
119                      element.getKind() + " annotated with "
120                      + AT + annotation.getSimpleName()
121                      + " but is not a subclass of "
122                      + supertype.getQualifiedName());
123            }
124            break;
125
126        default:
127            break;
128        }
129    }
130}