content: package mars.venus;
  
  import java.awt.event.KeyEvent;
  import java.lang.reflect.Modifier;
  import java.util.ArrayList;
  import java.util.HashMap;
  
  import javax.swing.JMenu;
  
  import mars.tools.MarsTool;
  import mars.util.FilenameFinder;
  
  /*
   * Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar
   *
   * Developed by Pete Sanderson (psanderson@otterbein.edu) and Kenneth Vollmar
   * (kenvollmar@missouristate.edu)
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   * SOFTWARE.
   *
   * (MIT license, http://www.opensource.org/licenses/mit-license.html)
   */
  
  /**
   * This class provides functionality to bring external Mars tools into the Mars
   * system by adding them to its Tools menu. This permits anyone with knowledge
   * of the Mars public interfaces, in particular of the Memory and Register
   * classes, to write applications which can interact with a MIPS program
   * executing under Mars. The execution is of course simulated. The private
   * method for loading tool classes is adapted from Bret Barker's GameServer
   * class from the book "Developing Games In Java".
   *
   * @author Pete Sanderson with help from Bret Barker
   * @version August 2005
   */
  
  public class ToolLoader {
  
  	private static final String CLASS_PREFIX = "mars.tools.";
  	private static final String TOOLS_DIRECTORY_PATH = "mars/tools";
  	private static final String TOOLS_MENU_NAME = "Tools";
  	private static final String MARSTOOL_INTERFACE = "MarsTool.class";
  	private static final String CLASS_EXTENSION = "class";
  
  	/**
  	 * Called in VenusUI to build its Tools menu. If there are no qualifying tools
  	 * or any problems accessing those tools, it returns null. A qualifying tool
  	 * must be a class in the Tools package that implements MarsTool, must be
  	 * compiled into a .class file, and its .class file must be in the same Tools
  	 * folder as MarsTool.class.
  	 *
  	 * @return a Tools JMenu if qualifying tool classes are found, otherwise null
  	 */
  	public JMenu buildToolsMenu() {
  		JMenu menu = null;
  		final ArrayList marsToolList = loadMarsTools();
  		if (!marsToolList.isEmpty()) {
  			menu = new JMenu(TOOLS_MENU_NAME);
  			menu.setMnemonic(KeyEvent.VK_T);
  			// traverse array list and build menu
  			MarsToolClassAndInstance listItem;
  			for (int i = 0; i < marsToolList.size(); i++) {
  				listItem = (MarsToolClassAndInstance) marsToolList.get(i);
  				menu.add(new ToolAction(listItem.marsToolClass, listItem.marsToolInstance.getName()));
  			}
  		}
  		return menu;
  	}
  
  	/*
  	*  Dynamically loads MarsTools into an ArrayList.  This method is adapted from
  	*  the loadGameControllers() method in Bret Barker's GameServer class.
  	*  Barker (bret@hypefiend.com) is co-author of the book "Developing Games
  	*  in Java".  It was demo'ed to me by Otterbein student Chris Dieterle
  	*  as part of his Spring 2005 independent study of implementing a networked
  	*  multi-player game playing system.  Thanks Bret and Chris!
  	*
  	*  Bug Fix 25 Feb 06, DPS: method did not recognize tools folder if its
  	*  absolute pathname contained one or more spaces (e.g. C:\Program Files\mars\tools).
  	*  Problem was, class loader's getResource method returns a URL, in which spaces
  	*  are replaced with "%20".  So I added a replaceAll() to change them back.
  	*
  	*  Enhanced 3 Oct 06, DPS: method did not work if running MARS from a JAR file.
  	*  The array of files returned is null, but the File object contains the name
  	*  of the JAR file (using toString, not getName).  Extract that name, open it
  	*  as a ZipFile, get the ZipEntry enumeration, find the class files in the tools
  	*  folder, then continue as before.
  	*/
  	private ArrayList loadMarsTools() {
  		final ArrayList toolList = new ArrayList();
  		final ArrayList candidates = FilenameFinder.getFilenameList(this.getClass().getClassLoader(),
  				TOOLS_DIRECTORY_PATH, CLASS_EXTENSION);
  		// Add any tools stored externally, as listed in Config.properties file.
  		// This needs some work, because mars.Globals.getExternalTools() returns
  		// whatever is in the properties file entry.  Since the class file will
  		// not be located in the mars.tools folder, the loop below will not process
  		// it correctly.  Not sure how to create a Class object given an absolute
  		// pathname.
  		//candidates.addAll(mars.Globals.getExternalTools());  // this by itself is not enough...
  		final HashMap tools = new HashMap();
  		for (int i = 0; i < candidates.size(); i++) {
  			final String file = (String) candidates.get(i);
  			// Do not add class if already encountered (happens if run in MARS development directory)
  			if (tools.containsKey(file)) {
  				continue;
  			} else {
  				tools.put(file, file);
  			}
  			if (!file.equals(MARSTOOL_INTERFACE)) {
  				try {
  					// grab the class, make sure it implements MarsTool, instantiate, add to menu
  					final String toolClassName = CLASS_PREFIX + file.substring(0, file.indexOf(CLASS_EXTENSION) - 1);
  					final Class clas = Class.forName(toolClassName);
  					if (!MarsTool.class.isAssignableFrom(clas) || Modifier.isAbstract(clas.getModifiers()) || Modifier
  							.isInterface(clas.getModifiers())) {
  						continue;
  					}
  					toolList.add(new MarsToolClassAndInstance(clas, (MarsTool) clas.newInstance()));
  				} catch (final Exception e) {
  					System.out.println("Error instantiating MarsTool from file " + file + ": " + e);
  				}
  			}
  		}
  		return toolList;
  	}
  
  	private class MarsToolClassAndInstance {
  
  		Class marsToolClass;
  		MarsTool marsToolInstance;
  
  		MarsToolClassAndInstance(final Class marsToolClass, final MarsTool marsToolInstance) {
  			this.marsToolClass = marsToolClass;
  			this.marsToolInstance = marsToolInstance;
  		}
  	}
  }
  