/*
 * Copyright 2006 Werner Guttmann
 * 
 * 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.exolab.javasource;

import java.util.HashMap;

/**
 * Makes an effort to identify where Override annotations belong. We are limited in what we look at
 * and we do not use reflection to identify if a class truly extends the appropriate class. We only
 * check the class of the arguments. This class may not be generically useful, but is useful with
 * the code generated by Castor. <br/>
 * Our one public method, {@link #addOverrideAnnotations(JMethodSignature)}, should only be called
 * when you already know that you are generating Java-5 style code.
 * 
 * @author <a href="mailto:werner DOT guttmann AT gmx DOT net">Werner Guttman</a>
 * @version $Revision: 5951 $ $Date: 2006-04-25 16:09:10 -0600 (Tue, 25 Apr 2006) $
 * @since 1.0.4
 */
public final class Java5HacksHelper {

  /**
   * As a static utility class, we want a private constructor.
   */
  private Java5HacksHelper() {}

  private static class MethodSpec {
    private String _methodName;
    private int _paramCount;
    private String _param1ClassName;
    private String _param2ClassName;

    public MethodSpec(final String methodName, final int paramCount, final String param1ClassName,
        final String param2ClassName) {
      _methodName = methodName;
      _paramCount = paramCount;
      _param1ClassName = param1ClassName;
      _param2ClassName = param2ClassName;
    }

    public String getMethodName() {
      return _methodName;
    }

    public int getParamCount() {
      return _paramCount;
    }

    public String getParam1ClassName() {
      return _param1ClassName;
    }

    public String getParam2ClassName() {
      return _param2ClassName;
    }
  }

  private static final HashMap<String, MethodSpec> DEFINED_SPECS =
      new HashMap<String, MethodSpec>();

  private static void createMethodSpec(final String methodName, final int paramCount,
      final String param1ClassName, final String param2ClassName) {
    MethodSpec temp = new MethodSpec(methodName, paramCount, param1ClassName, param2ClassName);

    DEFINED_SPECS.put(methodName, temp);
  }

  static {
    createMethodSpec("getAccessMode", 0, "", "");
    // createMethodSpec("getExtends", 0, "", "");
    // createMethodSpec("getIdentity", 0, "", "");
    createMethodSpec("getNameSpacePrefix", 0, "", "");
    createMethodSpec("getNameSpaceURI", 0, "", "");
    createMethodSpec("getValidator", 0, "", "");
    createMethodSpec("getXMLName", 0, "", "");
    createMethodSpec("getXTransients", 0, "", "");
    createMethodSpec("newInstance", 1, "java.lang.Object", "");
    createMethodSpec("setValue", 1, "java.lang.Object", "");
    createMethodSpec("equals", 1, "java.lang.Object", "");
    createMethodSpec("getValue", 1, "java.lang.Object", "");
    createMethodSpec("marshal", 1, "java.io.Writer", "");
    createMethodSpec("newInstance", 1, "java.lang.Object", "");
    createMethodSpec("setValue", 2, "java.lang.Object", "java.lang.Object");
    createMethodSpec("setXTransients", 1, "org.openmrm.core.data.castor.XTransients", "");
  };

  /** An override annotation we use to see if we can get others of its type. */
  private static final JAnnotationType OVERRIDE_ANNOTATION = new JAnnotationType("Override");

  /**
   * Given the method signature, add the Override annotation if this class is one that we know
   * requires this annotation.
   *
   * @param jms The method signature to inspect.
   */
  public static void addOverrideAnnotations(final JMethodSignature jms) {
    String name = jms.getName();
    boolean addOverrideAnnotation = false;

    // It the method already has an override annotation, then jump out
    JAnnotation override = jms.getAnnotation(OVERRIDE_ANNOTATION);
    if (override != null) {
      return;
    }

    // If the method name doesn't exist in our list, then jump out
    MethodSpec methodSpec = DEFINED_SPECS.get(name);
    if (methodSpec == null) {
      return;
    }

    // If the number of parameters isn't what we're prepared for, then jump out
    int paramCount = jms.getParameters().length;
    if (paramCount != methodSpec.getParamCount()) {
      return;
    }

    // Do we add the Override annotation? Check vs number of arguments.
    switch (paramCount) {
      case 0:
        addOverrideAnnotation = true;
        break;
      case 1:
        String incomingClassName1 = jms.getParameter(0).getType().getName();
        if (incomingClassName1.equalsIgnoreCase(methodSpec.getParam1ClassName())) {
          addOverrideAnnotation = true;
        }
        break;
      case 2:
        String className1 = jms.getParameter(0).getType().getName();
        String className2 = jms.getParameter(1).getType().getName();
        if (className1.equalsIgnoreCase(methodSpec.getParam1ClassName())
            && className2.equalsIgnoreCase(methodSpec.getParam2ClassName())) {
          addOverrideAnnotation = true;
        }
        break;
      default:
        // We aren't prepared for > 2 parameters, so we don't add an Override annotation
        break;
    }

    // Do the work if we need to
    if (addOverrideAnnotation) {
      jms.addAnnotation(new JAnnotation(new JAnnotationType("Override")));
    }
  }

}
