/*
 * Copyright 2006 Google Inc.
 *
 * 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 com.google.gwt.dev.util.xml;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * Retains parsed information about a particular schema clas.
 */
public class HandlerClassInfo {
  private static final HandlerMethod[] EMPTY_ARRAY_HANDLERMETHOD = new HandlerMethod[0];
  private static Map<Class<?>,HandlerClassInfo> sClassInfoMap =
    new HashMap<Class<?>,HandlerClassInfo>();

  public static synchronized HandlerClassInfo getClassInfo(Class<?> c) {
    if (sClassInfoMap.containsKey(c)) {
      return sClassInfoMap.get(c);
    } else {
      throw new RuntimeException("The schema class '" + c.getName()
          + "' should have been registered prior to parsing");
    }
  }

  public static synchronized void registerClass(Class<?> c) {
    if (sClassInfoMap.containsKey(c)) {
      return;
    }

    // Put a guard null in so that recursive registration of the same
    // class won't die.
    //
    sClassInfoMap.put(c, null);
    HandlerClassInfo classInfo = createClassInfo(c);
    sClassInfoMap.put(c, classInfo);
  }

  private static HandlerClassInfo createClassInfo(Class<?> c) {
    Map<String,HandlerMethod> namedHandlerMethods = new HashMap<String,HandlerMethod>();
    try {
      loadClassInfoRecursive(namedHandlerMethods, c);
    } catch (Exception e) {
      throw new RuntimeException("Unable to use class '" + c.getName()
          + "' as a handler", e);
    }
    HandlerClassInfo classInfo = new HandlerClassInfo(namedHandlerMethods);
    return classInfo;
  }

  private static void loadClassInfoRecursive(Map<String,HandlerMethod> namedHandlerMethods,
      Class<?> c) {
    if (!Schema.class.isAssignableFrom(c)) {
      // Have gone up as far as we can go.
      //
      return;
    }

    Method[] methods = c.getDeclaredMethods();
    for (int i = 0, n = methods.length; i < n; ++i) {
      Method method = methods[i];
      HandlerMethod handlerMethod = HandlerMethod.tryCreate(method);
      if (handlerMethod != null) {
        // Put in the map, but only if that method isn't already there.
        // (Allows inheritance where most-derived class wins).
        //
        String name = method.getName();
        if (!namedHandlerMethods.containsKey(name)) {
          namedHandlerMethods.put(name, handlerMethod);
        }
      }
    }

    // Recurse into superclass.
    //
    Class<?> superclass = c.getSuperclass();
    if (superclass != null) {
      loadClassInfoRecursive(namedHandlerMethods, superclass);
    }
  }

  private final Map<String,HandlerMethod> namedHandlerMethods;

  // Nobody else can create one.
  private HandlerClassInfo(Map<String,HandlerMethod> namedHandlerMethods) {
    this.namedHandlerMethods = namedHandlerMethods;
  }

  public HandlerMethod getEndMethod(String localName) {
    String methodName = "__" + localName.replace('-', '_');
    return namedHandlerMethods.get(methodName + "_end");
  }

  public HandlerMethod[] getHandlerMethods() {
    return namedHandlerMethods.values().toArray(EMPTY_ARRAY_HANDLERMETHOD);
  }

  public HandlerMethod getStartMethod(String localName) {
    String methodName = "__" + localName.replace('-', '_');
    return namedHandlerMethods.get(methodName + "_begin");
  }

  public HandlerMethod getTextMethod() {
    return namedHandlerMethods.get("__text");
  }
}
