/*
 * Copyright 2007 Peter Eibner
 *
 * 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.castor.core.annotationprocessing;

import java.lang.annotation.*;

import junit.framework.TestCase;

import org.castor.core.nature.BaseNature;
import org.castor.core.nature.PropertyHolder;
import org.castor.core.annotationprocessing.AnnotationProcessor;
import org.castor.core.annotationprocessing.BaseAnnotationProcessingService;

public class BaseAnnotationProcessingServiceTest extends TestCase {

    @Retention(RetentionPolicy.RUNTIME)
    public @interface SupportedAnnotation {
    }

    @Retention(RetentionPolicy.RUNTIME)
    public @interface UnsupportedAnnotation {
    }

    /**
     * This is a {@link AnnotationProcessor} that actually does nothing, except
     * telling that it processed the given Annotation if it was of Type
     * {@link SupportedAnnotation}. It's just a dummy.
     */
    class SupportedAnnotationProcessor implements AnnotationProcessor {

        public Class<? extends Annotation> forAnnotationClass() {
            return SupportedAnnotation.class;
        }

        public <I extends BaseNature, A extends Annotation> boolean processAnnotation(
                I info, A annotation) {
            if (annotation instanceof SupportedAnnotation) {
                // do
                return true;
            }
            return false;
        }
    }

    /**
     * A simple empty class, that just has 2 Annotations - for one (
     * {@link SupportedAnnotation}) we have a dummy processor, for the other one
     * ( {@link UnsupportedAnnotation}) we don't have a processor.
     * 
     */
    @SupportedAnnotation
    @UnsupportedAnnotation
    class AnnotationHolder {

    }

    /**
     * A dummy property holder needed for use with {@link BaseNature}s. In real
     * usage within Castor, this would be a ClassInfo or FieldInfo.
     * 
     */
    class MyPropertyHolder implements PropertyHolder {

        public Object getProperty(String arg0) {
            return null;
        }

        public void setProperty(String arg0, Object arg1) {
        }

        public void addNature(String arg0) {
        }

        public boolean hasNature(String arg0) {
            return true;
        }

    }

    @Override
    protected void setUp() throws Exception {
        assertTrue(AnnotationHolder.class
                .isAnnotationPresent(SupportedAnnotation.class));
        assertTrue(AnnotationHolder.class
                .isAnnotationPresent(UnsupportedAnnotation.class));

    }

    public void testAddGetAnnotationProcessor() {
        BaseAnnotationProcessingService baps = new BaseAnnotationProcessingService();
        SupportedAnnotationProcessor annotationProcessor = new SupportedAnnotationProcessor();

        assertNotNull(baps.getAnnotationProcessors());
        assertFalse(baps.getAnnotationProcessors()
                .contains(annotationProcessor));

        baps.addAnnotationProcessor(annotationProcessor);

        assertNotNull(baps.getAnnotationProcessors());
        assertTrue(baps.getAnnotationProcessors().contains(annotationProcessor));

    }

    public void testProcessAnnotations() {
        BaseAnnotationProcessingService baps = new BaseAnnotationProcessingService();
        SupportedAnnotationProcessor annotationProcessor = new SupportedAnnotationProcessor();
        baps.addAnnotationProcessor(annotationProcessor);
        Annotation[] annotations = AnnotationHolder.class.getAnnotations();
        assertEquals(2, annotations.length);
        /*
         * now annotations[] contains @SupportedAnnotation and
         * @UnsupportedAnnotation
         */

        MyPropertyHolder holder = new MyPropertyHolder();
        holder.addNature(BaseNature.class.getName());

        BaseNature info = new BaseNature(holder) {
            public String getId() {
                return BaseNature.class.getName();
            }
        };

        /*
         * try to process all annotations
         */
        Annotation[] unprocessed = baps.processAnnotations(info, annotations);

        /*
         * we know, that we don't have processor for @UnsupportedAnnotation, so
         * 
         * @UnsupportedAnnotation is put into unprocessed[]
         */
        assertNotNull(unprocessed);
        assertEquals(1, unprocessed.length);
        assertEquals(UnsupportedAnnotation.class, unprocessed[0]
                .annotationType());
    }

    public void testProcessAnnotation() {
        BaseAnnotationProcessingService baps = new BaseAnnotationProcessingService();
        baps.addAnnotationProcessor(new SupportedAnnotationProcessor());

        MyPropertyHolder holder = new MyPropertyHolder();
        holder.addNature(BaseNature.class.getName());
        BaseNature info = new BaseNature(holder) {
            public String getId() {
                return BaseNature.class.getName();
            }
        };

        /*
         * our AnnotationHolder has 2 Annotations: @UnsupportedAnnotation and
         * 
         * @SupportedAnnotation, so we get them
         */
        Annotation unsupportedAnnotation = AnnotationHolder.class
                .getAnnotation(UnsupportedAnnotation.class);
        Annotation supportedAnnotation = AnnotationHolder.class
                .getAnnotation(SupportedAnnotation.class);
        assertNotNull(unsupportedAnnotation);
        assertNotNull(supportedAnnotation);

        /*
         * we know, that we don't have processor for @UnsupportedAnnotation, so
         * processing it will return false.
         */
        assertFalse(baps.processAnnotation(info, unsupportedAnnotation));
        assertTrue(baps.processAnnotation(info, supportedAnnotation));

    }

}
