Package cero.plugin

Source Code of cero.plugin.PluginManager

/*
*  Cero Project - Copyright   2006 The Cero Developement Team
*  (Michael Laguerre, Camille Roux, Matthieu Segret, Mathieu Sivade)
*
*  This program is free software; you can redistribute it and/or modify it
*  under the terms of the GNU General Public License as published by the Free
*  Software Foundation; either version 2 of the License, or (at your option)
*  any later version.
*
*  This program is distributed in the hope that it will be useful, but
*  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
*  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
*  for more details.
*/

package cero.plugin;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import cero.UnsupportedPluginException;
import cero.games.AIPlayer;
import cero.games.Game;
import cero.games.GameInitializer;
import cero.games.Player;
import cero.games.Rule;
import cero.games.base.PlayerBase;
import cero.ui.InterfaceModule;
import cero.ui.UserInterface;

/**
* This class gives you acces to all the plugins you might want to use. It is a
* singleton, and you must *NOT* try to instanciate it, as it will throw an
* Exception if you do. Use getInstance instead to get a reference on the
* PluginManager.
*
* @author Telem
*/
public class PluginManager {
  /** The collection of user interfaces */
  private Collection<InterfacePlugin> uiPlugins = new ArrayList<InterfacePlugin>();

  /** The collection of modules designed for user interfaces */
  private Collection<InterfaceModulePlugin> uiPluginModules = new ArrayList<InterfaceModulePlugin>();

  /** The collection of games mapped by their name */
  private Map<String, GamePlugin> gamePlugins = new HashMap<String, GamePlugin>();

  // FIXME : à faire disparaitre
  /**
   * The collection of the UI instances used, currently needed by PlayerBase
   * to return a ChoiceMaker
   */
  private Collection<UserInterface> uiInstances = new ArrayList<UserInterface>();

  private static PluginManager instance = null;

  public PluginManager() throws InstantiationException {
    if (instance != null)
      throw new InstantiationException(
          "PluginManager is a Singleton, you can't create more than one instance");
    instance = this;
  }

  public static PluginManager getInstance() {
    if (instance == null) {
      try {
        new PluginManager();
      } catch (InstantiationException e) {
        // we're sure we'll never get there as long as we don't use
        // multithreading
      }
    }
    return instance;
  }

  public void loadPath(String path) {
    loadPath(path, 0);
  }

  public void loadPath(String path, int recDeepness) {
    loadPath(path, recDeepness, ClassLoader.getSystemClassLoader());
  }

  private void loadPath(String path, int recDeepness, ClassLoader parentcl) {
    if (recDeepness < 0)
      recDeepness = -1;

    File file = new File(path);
    DynamicURLClassLoader durlcl = new DynamicURLClassLoader(new URL[0],
        parentcl);

    String[] filesToLoad = null;
    if (file.isDirectory())
      filesToLoad = file.list();
    else {
      filesToLoad = new String[1];
      filesToLoad[0] = file.getAbsolutePath();
    }

    if (filesToLoad != null) {
      Collection<File> directories = new ArrayList<File>();
      File tmpfile;
      for (int i = 0; i < filesToLoad.length; i++) {
        tmpfile = new File(path + "/" + filesToLoad[i]);
        if (tmpfile.isDirectory())
          directories.add(tmpfile);
        else if (filesToLoad[i].endsWith(".jar")) {
          // if it is a file, loads each plugin contained in it
          try {
            durlcl.addURL(new URL("file://"
                + file.getAbsolutePath()));
            Set<String> loadedClassesNames = listClassesInJar(tmpfile);
            Set<Class<?>> loadedClasses = new HashSet<Class<?>>();
            for (String name : loadedClassesNames)
              loadedClasses.add(durlcl.loadClass(name));

            loadGamePlugin(loadedClasses, durlcl);
            loadInterfacePlugins(loadedClasses);
            loadInterfaceModulePlugins(loadedClasses);
          } catch (IOException e1) {
            // the file should be accessible, theorically. else, it
            // will rely on the error system not yet implemented
            e1.printStackTrace();
          } catch (BrokenPluginException e) {
            // we should inform the user
            e.printStackTrace();
          } catch (ClassNotFoundException e) {
            // there is a .class entry in the jar that is not a clas
            e.printStackTrace();
          }
        }
      }

      // once the files of the folder are loaded, we load the subfolders
      // if needed
      if (recDeepness != 0)
        for (File f : directories)
          loadPath(f.getAbsolutePath(), recDeepness - 1, durlcl);

    }
  }

  private Set<String> listClassesInJar(File jarfile) throws IOException {
    Set<String> classes = new HashSet<String>();
    JarFile jf = new JarFile(jarfile);
    Enumeration<JarEntry> jarEntries = jf.entries();
    while (jarEntries.hasMoreElements()) {
      String entryname = jarEntries.nextElement().getName();
      if (entryname.endsWith(".class"))
        classes.add(entryname.replace("/", ".").substring(0,
            entryname.length() - 6));
    }
    return classes;
  }

  private static boolean canBeInstanciable(Class<?> c) {
    final int notInstantiableClassMask = Modifier.ABSTRACT
        | Modifier.INTERFACE | Modifier.PRIVATE | Modifier.PROTECTED;
    if ((c.getModifiers() & notInstantiableClassMask) != 0)
      return false;
    if ((c.getModifiers() & Modifier.PUBLIC) == 0)
      return false;
    if (c.isLocalClass())
      return false;
    if (c.isMemberClass())
      return false;
    return true;
  }

  private void loadGamePlugin(Set<Class<?>> classes, ClassLoader myLoader)
      throws BrokenPluginException {
    Class<? extends Game> gameClass = null;
    Class<? extends Player> playerClass = null;
    Collection<Class<? extends Rule>> rulesClass = new ArrayList<Class<? extends Rule>>();
    Collection<Class<? extends GameInitializer>> initClass = new ArrayList<Class<? extends GameInitializer>>();
    Collection<Class<? extends AIPlayer>> aisClass = new ArrayList<Class<? extends AIPlayer>>();

    for (Class<?> c : classes) {
      if (!canBeInstanciable(c))
        continue;
      if (Game.class.isAssignableFrom(c))
        gameClass = c.asSubclass(Game.class);
      else if (AIPlayer.class.isAssignableFrom(c))
        aisClass.add(c.asSubclass(AIPlayer.class));
      else if (Player.class.isAssignableFrom(c))
        playerClass = c.asSubclass(Player.class);
      else if (Rule.class.isAssignableFrom(c))
        rulesClass.add(c.asSubclass(Rule.class));
      else if (GameInitializer.class.isAssignableFrom(c))
        initClass.add(c.asSubclass(GameInitializer.class));
    }

    try {
      if (gameClass == null)
        throw new BrokenPluginException(
            "Plugin is missing an instantiable Game class");
      if (playerClass == null)
        playerClass = myLoader.loadClass("cero.games.base.PlayerBase")
            .asSubclass(PlayerBase.class);
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
      throw new RuntimeException("Couldn't find PlayerBase");
    }

    // GamePlugin construction
    GamePlugin gp;
    try {
      gp = new GamePlugin(gameClass);
      gp.setPlayerType(playerClass);
    } catch (NotInstantiableException e) {
      // if any of these aren't instantiable, the game can't be played
      throw new BrokenPluginException(e.getMessage());
    }
    try {
      for (Class<? extends AIPlayer> c : aisClass)
        gp.addAIType(c);
      for (Class<? extends Rule> c : rulesClass)
        gp.addRule(c);
      for (Class<? extends GameInitializer> c : initClass)
        gp.addInitializer(c);
    } catch (NotInstantiableException e) {
      // if any of these aren't instantiable, the user should be warned
      // yet the game can be played
      e.printStackTrace();
    }
    gamePlugins.put(gp.getPluginName(), gp);
  }

  private void loadInterfacePlugins(Set<Class<?>> classes) {
    for (Class<?> c : classes) {
      if (canBeInstanciable(c) && UserInterface.class.isAssignableFrom(c)) {
        try {
          uiPlugins.add(new InterfacePlugin(c
              .asSubclass(UserInterface.class)));
        } catch (Exception e) {
          // we should inform the user
          e.printStackTrace();
        }
      }
    }
  }

  private void loadInterfaceModulePlugins(Set<Class<?>> classes) {
    for (Class<?> c : classes) {
      if (canBeInstanciable(c)
          && InterfaceModule.class.isAssignableFrom(c)) {
        try {
          uiPluginModules.add(new InterfaceModulePlugin(c
              .asSubclass(InterfaceModule.class)));
        } catch (NotInstantiableException e) {
          // TODO print down the modules which couldn't be found
          e.printStackTrace();
        }
      }
    }
  }

  public Collection<String> getGamesName() {
    return new ArrayList<String>(gamePlugins.keySet());
  }

  public Collection<String> getAIsName(String gameName) {
    GamePlugin gp = gamePlugins.get(gameName);
    return (gp == null)?null:gp.getAINames();
  }

  public Collection<String> getInterfacesName() {
    Collection<String> ui = new ArrayList<String>();
    for (InterfacePlugin uip : uiPlugins) {
      ui.add(uip.getPluginName());
    }
    return ui;
  }

  public Game getNewGame(String gameName) {
    GamePlugin gp = gamePlugins.get(gameName);
    if (gp == null)
      return null;

    Game game = gp.newGameInstance();
    // adds the interface to the game, so that they become aware of it
    for (UserInterface ui : uiInstances)
      game.addGameListener(ui);

    return game;
  }

  public Player getNewPlayer(String gameName) {
    GamePlugin gp = gamePlugins.get(gameName);
    return (gp == null)?null:gp.newPlayer();
  }

  public AIPlayer getNewAI(String gameName, String AIName) {
    GamePlugin gp = gamePlugins.get(gameName);
    return (gp == null)?null:gp.newAIPlayer(AIName);
  }

  public Collection<GameInitializer> getGameInitializers(String gameName) {
    GamePlugin gp = gamePlugins.get(gameName);
    return (gp == null)?null:gp.getInitializers();
  }

  public Collection<Rule> getGameRules(String gameName) {
    GamePlugin gp = gamePlugins.get(gameName);
    return (gp == null)?null:gp.getRules();
  }

  public UserInterface getNewUserInterface(String uiName) {
    InterfacePlugin founduip = null;
    for (InterfacePlugin uip : uiPlugins) {
      if (uip.getPluginName().equals(uiName))
        founduip = uip;
    }
    if (founduip == null)
      return null;

    UserInterface uiinst = founduip.newInterfaceInstance();
    for (InterfaceModulePlugin imp : uiPluginModules) {
      if (imp.getInterfaceType().isInstance(uiinst))
        try {
          uiinst.addInterfaceModule(imp.newModuleInstance());
        } catch (UnsupportedPluginException e) {
          // TODO we should inform the user the plugin is
          // incoherent with the interface
        }
    }
    uiInstances.add(uiinst);
    return uiinst;
  }

  public Collection<UserInterface> getInterfaceInstances() {
    return new ArrayList<UserInterface>(uiInstances);
  }

}
TOP

Related Classes of cero.plugin.PluginManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.