/*
 * Copyright 2003-2011 JetBrains s.r.o.
 *
 * 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 jetbrains.mps.util;

import java.util.HashMap;
import java.util.Map;

public abstract class AbstractClassLoader extends ClassLoader {
  private Map<String, Class> myCache = new HashMap<String, Class>();

  public AbstractClassLoader(ClassLoader classLoader) {
    super(classLoader);
  }

  protected abstract byte[] findClassBytes(String name);

  protected abstract boolean isExcluded(String name);

  @Override
  protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    Class c = myCache.get(name);

    if (myCache.containsKey(name) && c == null) {
      throw new ClassNotFoundException(name);
    }

    if (c == null) {
      byte[] bytes = null;

      if (!isExcluded(name)) {
        bytes = findClassBytes(name);
      }

      if (bytes == null) {
        try {
          c = getParent().loadClass(name);
        } catch (ClassNotFoundException e) {
          myCache.put(name, null);
          throw e;
        }
        if (resolve) {
          resolveClass(c);
        }
      } else {
        definePackageIfNecessary(name);
        c = defineClass(name, bytes, 0, bytes.length, ProtectionDomainUtil.loadedClassDomain());
      }
      myCache.put(name, c);
    }
    if (resolve) {
      resolveClass(c);
    }
    return c;
  }


  private void definePackageIfNecessary(String name) {
    String pack = NodeNameUtil.getNamespace(name);
    if (getPackage(pack) == null) {
      definePackage(pack, null, null, null, null, null, null, null);
    }
  }
}
