package de.tu_darmstadt.tk.SmartHomeNetworkSim.control; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.LinkedList; import javax.tools.JavaCompiler; import javax.tools.ToolProvider; import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Link; import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Model; import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Connection; import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.ProbabilityDistributionHandler; import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Protocol; import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.SmartDevice; import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.configuration.ImportConfiguration; /** * Controller which manages Imported Classes of the framework * * @author Andreas T. Meyer-Berg */ public class ImportController { Model model; Controller controller; ImportConfiguration importConf; /** * Creates a new ImportController * * @param model * model to be edited * @param controller * parent controller */ public ImportController(Model model, Controller controller) { this.model = model; this.controller = controller; this.importConf = controller.getSettingsController().getConfigurationManager().getImportConfiguration(); } /** * Adds new Link to the model * * @param Link * Link to be added * @return true if it was added */ public boolean addLink(Class Link) { if (isValidLink(Link)) importConf.addLinkClass(Link); else return false; return true; } /** * Removes Link from the model * * @param Link * Link to be removed */ public void removeLink(Class Link) { importConf.removeLinkClass(Link); } /** * Returns the available Links of the model * * @return available links */ public LinkedList> getLinks() { return importConf.getLinkClasses(); } /** * Returns true if it is a Valid Link, false if not * * @param link * Link to be checked * @return true if it is a valid Link */ public boolean isValidLink(Class link) { try { /** * Link to be tested */ Link l = link.newInstance(); // Empty constructor required, to create new instance if (l == null) throw new Exception("Link required an empty constructor"); // Name shall not be null or empty string if (l.getName() == null || l.getName() == "") throw new Exception( "Link name shall not be null or empty string."); } catch (Exception e) { return false; } return true; } /** * Adds new Protocol to the model * * @param protocol * protocol to be added * @return true if it was added */ public boolean addProtocol(Class protocol) { if (isValidProtocol(protocol)) importConf.addProtocolClass(protocol); else return false; return true; } /** * Removes protocol from the model * * @param protocol * protocol to be removed */ public void removeProtocol(Class protocol) { importConf.removeProtocolClass(protocol); } /** * Returns the available Protocols of the model * * @return available protocols */ public LinkedList> getProtocols() { return importConf.getProtocolClasses(); } /** * Returns true if it is a Valid Protocol, false if not * * @param protocol * protocol to be checked * @return true if it is a valid protocol */ public boolean isValidProtocol(Class protocol) { try { /** * Protocol to be tested */ Protocol p = protocol.newInstance(); // Empty constructor required, to create new instance if (p == null) throw new Exception("Protocol required an empty constructor"); // Name shall not be null or empty string if (p.getName() == null || p.getName() == "") throw new Exception( "Protocol name shall not be null or empty string."); if (p.getRoles() == null || p.getRoles().length == 0) throw new Exception("Roles shall not be null or empty"); // Number of roles have to match if (p.getNumberOfRoles() != p.getRoles().length) throw new Exception( "getNumberOfRoles() does not match getRoles().length"); } catch (Exception e) { return false; } return true; } /** * Adds new Connection to the model * * @param connection * Connection to be added * @return true if it was added */ public boolean addConnection(Class connection) { if (isValidConnection(connection)) importConf.addConnectionClass(connection); else return false; return true; } /** * Removes Connection from the model * * @param connection * Connection to be removed */ public void removeConnection(Class connection) { importConf.removeConnectionClass(connection); } /** * Returns the available Connections of the model * * @return available Connections */ public LinkedList> getConnections() { return importConf.getConnectionClasses(); } /** * Returns true if it is a Valid Connection, false if not * * @param connection * Connection to be checked * @return true if it is a valid Connection */ public boolean isValidConnection(Class connection) { try { /** * Connection to be tested */ Connection p = connection.newInstance(); // Empty constructor required, to create new instance if (p == null) throw new Exception("Connection required an empty constructor"); // Name shall not be null or empty string if (p.getName() == null || p.getName() == "") throw new Exception( "Connection name shall not be null or empty string."); } catch (Exception e) { return false; } return true; } /** * Adds new SmartDevice to the model * * @param smartDevice * SmartDevice to be added * @return true if it was added */ public boolean addSmartDevice(Class smartDevice) { if (isValidSmartDevice(smartDevice)) importConf.addSmartDeviceClass(smartDevice); else return false; return true; } /** * Removes SmartDevice from the model * * @param smartDevice * SmartDevice to be removed */ public void removeSmartDevice(Class smartDevice) { importConf.removeSmartDeviceClass(smartDevice); } /** * Returns the available SmartDevices of the model * * @return available SmartDevices */ public LinkedList> getSmartDevices() { return importConf.getSmartDeviceClasses(); } /** * Returns true if it is a Valid SmartDevice, false if not * * @param smartDevice * SmartDevice to be checked * @return true if it is a valid SmartDevice */ public boolean isValidSmartDevice(Class smartDevice) { try { /** * SmartDevice to be tested */ SmartDevice p = smartDevice.newInstance(); // Empty constructor required, to create new instance if (p == null) throw new Exception("SmartDevice required an empty constructor"); // Name shall not be null or empty string if (p.getName() == null || p.getName() == "") throw new Exception( "SmartDevice name shall not be null or empty string."); } catch (Exception e) { return false; } return true; } /** * Adds new DistributionHandler to the model * * @param distributionHandler DistributionHandler to be added * @return true if it was added */ public boolean addDistributionHandler(Class distributionHandler) { if (isValidDistributionHandler(distributionHandler)) importConf.addDistributionHandlerClass(distributionHandler); else return false; return true; } /** * Removes DistributionHandler from the model * * @param distributionHandler * DistributionHandler to be removed */ public void removeDistributionHandler(Class distributionHandler) { importConf.removeDistributionHandlerClass(distributionHandler); } /** * Returns the available DistributionHandler of the model * * @return available DistributionHandler */ public LinkedList> getDistributionHandlers() { return importConf.getDistributionHandlerClasses(); } /** * Returns true if it is a Valid DistributionHandler, false if not * * @param distributionHandler * DistributionHandler to be checked * @return true if it is a valid DistributionHandler */ public boolean isValidDistributionHandler(Class distributionHandler) { try { /** * SmartDevice to be tested */ ProbabilityDistributionHandler p = distributionHandler.newInstance(); // Empty constructor required, to create new instance if (p == null) throw new Exception("DistributionHandler required an empty constructor"); // Name shall not be null or empty string if (p.getSimpleDescription() == null || p.getSimpleDescription() == "") throw new Exception( "DistributionHandler name shall not be null or empty string."); } catch (Exception e) { return false; } return true; } /** * Imports the given .java File, compiles it and returns the compiled class * * @param javaFile File(path) * @return Class which was compiled * @throws ClassImportException if an problem occurred during import */ public static Class importJavaClass(File javaFile) throws ClassImportException{ /** * Package name */ String packageName = getJavaPackageName(javaFile); if (packageName == null) { // if package null - try default package packageName = ""; } /** * Name of the Class. File name "ClassName.java" */ String className = javaFile.getName().substring(0, javaFile.getName().lastIndexOf('.')); /** * Compiler, to compile the File */ JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); compiler.run(null, null, null, javaFile.getPath()); /** * Root File of the imported Protocol (if the Java File is inside a package) */ File root = javaFile.getParentFile(); if(!packageName.isEmpty()){ /** * Array of all parent package names e.g. "pack1.pack2.pack3" -> {"pack1","pack2","pack3"} */ String[] packageFolders = packageName.split("\\."); if(packageFolders.length<2){ //A single level if(root.getName().compareTo(packageName)!=0){ throw new ClassImportException("Invalid ParentFolderName: Expected \""+packageName+"\" but was \""+root.getName()+"\""); } root = root.getParentFile(); }else{ for(int i = packageFolders.length-1;i>=0;i--){ if(root.getName().compareTo(packageFolders[i])!=0){ throw new ClassImportException("Invalid ParentFolderName at level "+i+": Expected \""+packageFolders[i]+"\" but was \""+root.getName()+"\""); } root = root.getParentFile(); } } } /** * ClassLoader to load the compiled class, given it's root */ URLClassLoader classLoader; try { classLoader = URLClassLoader .newInstance(new URL[] { root.toURI().toURL() }); } catch (MalformedURLException e1) { throw new ClassImportException("Invalid URL/File at the root of the imported Class"); } // If packageName is not empty, and dot is required: e.g. "packagePath.ClassName" if (!packageName.isEmpty()) packageName += "."; /** * Imported Class */ Class cls = null; try{ cls = Class.forName(packageName + className, true, classLoader); }catch(ClassNotFoundException e){ try{ //Second try to load class cls = classLoader.loadClass(packageName + className); }catch(ClassNotFoundException e2){ throw new ClassImportException("Class not found: "+packageName+className); } } return cls; } /** * Returns the package path of a given Java File. Returns null if the * exception occurred and returns an empty string if no package declaration * was found. Package declaration should be in a single line. * * @param javaFile * File to be searched for the package path * @return PackagePath of the JavaFile, EmptyString, if no declaration was * found, null on errors */ public static String getJavaPackageName(File javaFile) { /** * If javaFile null / non existent -> return null */ if(javaFile==null||!javaFile.exists()) return null; /** * Package name */ String packageName = ""; /** * Reader to read the java File */ BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(javaFile.getPath())); /** * Current Line which is investigated */ String currentLine = reader.readLine(); // Search each line until a valid package is found while (currentLine != null) { currentLine = currentLine.trim(); if (!currentLine.isEmpty()) { //Check if line begins with package if (currentLine.length() >= 7 && currentLine.startsWith("package")) { packageName = currentLine.substring(8, currentLine.length() - 1); } } if (packageName.isEmpty()) { currentLine = reader.readLine(); } else { currentLine = null; } } } catch (Exception e) { return null; } finally { try { if(reader != null) reader.close(); } catch (IOException e) { } } // Remove whitespace in cases like "import test.package ;" packageName = packageName.trim(); return packageName; } }