/*
 * Copyright 2003-2015 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.module;

import jetbrains.mps.classloading.ClassLoaderManager;
import jetbrains.mps.classloading.ModuleClassNotFoundException;
import jetbrains.mps.classloading.ModuleIsNotLoadableException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.module.SModule;

/**
 * Represents a module which can be associated with some class loader.
 *
 * For example suppose there is a language module L in MPS.
 * Also let there be a solution S which uses the language L. Imagine that at some point you decide to
 * change the language L, e.g. change the editor representation for some concept C in the language L.
 * Obviously you expect MPS to change the UI appearance for the instances of the concept C in the solution S.
 * Moreover you want MPS to change the UI representation right after the used language L is generated and compiled.
 *
 * To enable such workflow MPS introduces its own class loading subsystem. Also it brings in a notion of reloadable modules --
 * such modules which can be redeployed during design-time in MPS.
 * So the language L in the given example is clearly a reloadable module.
 */
public interface ReloadableModule extends SModule {
  /**
   * @return a class which can be obtained by calling #getclass from
   * {@link #getClassLoader()} method.
   * a ModuleClassLoader. ModuleClassLoader's #loadClass method yields some additional information
   * about the reasons of class which could not be found.
   * Clients of this API are supposed to process it on their own behalf.
   *
   * @see jetbrains.mps.classloading.ModuleClassNotFoundException
   * @see jetbrains.mps.classloading.ModuleIsNotLoadableException
   * warning: this method is lazy implemented!
   */
  @NotNull
  Class<?> getClass(String classFqName) throws ClassNotFoundException, ModuleClassNotFoundException, ModuleIsNotLoadableException;

  /**
   * @return a class which can be obtained by calling #getClass from
   * {@link #getClassLoader()} method in the case when the defining class loader is
   * a ModuleClassLoader.
   *
   * @see jetbrains.mps.classloading.ModuleClassNotFoundException
   * @see jetbrains.mps.classloading.ModuleIsNotLoadableException
   * @see jetbrains.mps.classloading.ModuleClassLoader
   * @see jetbrains.mps.classloading.ModuleClassLoader#loadOwnClass(String)
   * warning: this method is lazy implemented!
   */
  @NotNull
  Class<?> getOwnClass(String classFqName) throws ClassNotFoundException, ModuleClassNotFoundException, ModuleIsNotLoadableException;

  /**
   * @return the class loader associated with the module.
   * Currently it can be either MPS ModuleClassLoader or IdeaPlugin PluginClassLoader.
   *
   * The latter is returned in the case when IDEA plugin manages the module's classes.
   * Use it if you want to get a class from the module with IdeaPluginFacet.
   * warning: this method is lazy implemented!
   */
  @Nullable
  ClassLoader getClassLoader();

  /**
   * @return so-called parent (or root) class loader. Simply returns the Idea plugin classloader in the case the module is
   * bundled into an idea plugin. Will return an application classloader in the case there is no idea plugin.
   * Contract: the class loader returned from #getClassLoader always depends on the root class loader.
   */
  ClassLoader getRootClassLoader();

  /**
   * Call it to replace the old class loader of this module with a new one.
   * To reload more than one module all together
   * check out {@link ClassLoaderManager#reloadModules(Iterable)} method.
   */
  void reload();

  /**
   * @return true if it will load classes.
   * For some subclasses it is possible to disable class loading for <code>ReloadableModule</code>.
   * E.g. solution without idea/mps facet cannot load classes
   * @see jetbrains.mps.project.Solution
   */
  boolean willLoad();
}
