/*
 * Copyright 2002-2008 the original author or authors.
 *
 * 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.springframework.config.java.internal.model;

import static java.lang.String.format;

import org.springframework.config.java.model.ModelMethod;

import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;

import java.util.List;


/** TODO: JAVADOC */
public abstract class JavaConfigMethod extends ModelMethod {

    public JavaConfigMethod(String name, int modifiers, Annotation[] annotations) {
        super(name, modifiers, annotations);
    }

    /**
     * All JavaConfig methods are associated with an identifying annotation.
     */
    public abstract Annotation getMetadata();

    public void detectUsageErrors(List<UsageError> errors) {
        if (Modifier.isPrivate(getModifiers()))
            // TODO: needs to have reference to parent class for better diagnostics
            errors.add(new PrivateMethodError());

        if (Modifier.isFinal(getModifiers()))
            errors.add(new FinalMethodError());
    }

    public class IncompatibleAnnotationError extends UsageError {
        private final Annotation incompatibleAnno;

        public IncompatibleAnnotationError(Annotation incompatibleAnno) {
            super(getDeclaringClass(), getLineNumber());
            this.incompatibleAnno = incompatibleAnno;
        }

        @Override
        public String getDescription() {
            String incompatibleAnnoName = incompatibleAnno.annotationType().getSimpleName();
            return format("@%s method '%s' is not compatible with @%s annotation. Remove the @%s annotation to continue.",
                          getMetadata().annotationType().getSimpleName(), getName(),
                          incompatibleAnnoName, incompatibleAnnoName);
        }
    }

    /** JavaConfigMethods must be visible (non-private) in order to accommodate CGLIB. */
    public class PrivateMethodError extends UsageError {
        public PrivateMethodError() {
            super(getDeclaringClass(), getLineNumber());
        }

        @Override
        public String getDescription() {
            return format("@%s method '%s' may not be private. If bean hiding is desired, "
                          + "consider making this method protected or package-private",
                          getMetadata().annotationType().getSimpleName(), getName());
        }
    }

    /** JavaConfigMethods must be extensible (non-final) in order to accommodate CGLIB. */
    public class FinalMethodError extends UsageError {
        public FinalMethodError() {
            super(getDeclaringClass(), getLineNumber());
        }

        @Override
        public String getDescription() {
            return format("@%s method '%s' may not be final. Remove the final modifier to continue.",
                          getMetadata().annotationType().getSimpleName(), getName());
        }
    }

}
