ImportController.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. package de.tu_darmstadt.tk.SmartHomeNetworkSim.control;
  2. import java.io.BufferedReader;
  3. import java.io.File;
  4. import java.io.FileReader;
  5. import java.io.IOException;
  6. import java.net.MalformedURLException;
  7. import java.net.URL;
  8. import java.net.URLClassLoader;
  9. import java.util.LinkedList;
  10. import javax.tools.JavaCompiler;
  11. import javax.tools.ToolProvider;
  12. import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Link;
  13. import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Model;
  14. import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Connection;
  15. import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.ProbabilityDistributionHandler;
  16. import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Protocol;
  17. import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.SmartDevice;
  18. import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.configuration.ImportConfiguration;
  19. /**
  20. * Controller which manages Imported Classes of the framework
  21. *
  22. * @author Andreas T. Meyer-Berg
  23. */
  24. public class ImportController {
  25. Model model;
  26. Controller controller;
  27. ImportConfiguration importConf;
  28. /**
  29. * Creates a new ImportController
  30. *
  31. * @param model
  32. * model to be edited
  33. * @param controller
  34. * parent controller
  35. */
  36. public ImportController(Model model, Controller controller) {
  37. this.model = model;
  38. this.controller = controller;
  39. this.importConf = controller.getSettingsController().getConfigurationManager().getImportConfiguration();
  40. }
  41. /**
  42. * Adds new Link to the model
  43. *
  44. * @param Link
  45. * Link to be added
  46. * @return true if it was added
  47. */
  48. public boolean addLink(Class<? extends Link> Link) {
  49. if (isValidLink(Link))
  50. importConf.addLinkClass(Link);
  51. else
  52. return false;
  53. return true;
  54. }
  55. /**
  56. * Removes Link from the model
  57. *
  58. * @param Link
  59. * Link to be removed
  60. */
  61. public void removeLink(Class<? extends Link> Link) {
  62. importConf.removeLinkClass(Link);
  63. }
  64. /**
  65. * Returns the available Links of the model
  66. *
  67. * @return available links
  68. */
  69. public LinkedList<Class<? extends Link>> getLinks() {
  70. return importConf.getLinkClasses();
  71. }
  72. /**
  73. * Returns true if it is a Valid Link, false if not
  74. *
  75. * @param link
  76. * Link to be checked
  77. * @return true if it is a valid Link
  78. */
  79. public boolean isValidLink(Class<? extends Link> link) {
  80. try {
  81. /**
  82. * Link to be tested
  83. */
  84. Link l = link.newInstance();
  85. // Empty constructor required, to create new instance
  86. if (l == null)
  87. throw new Exception("Link required an empty constructor");
  88. // Name shall not be null or empty string
  89. if (l.getName() == null || l.getName() == "")
  90. throw new Exception(
  91. "Link name shall not be null or empty string.");
  92. } catch (Exception e) {
  93. return false;
  94. }
  95. return true;
  96. }
  97. /**
  98. * Adds new Protocol to the model
  99. *
  100. * @param protocol
  101. * protocol to be added
  102. * @return true if it was added
  103. */
  104. public boolean addProtocol(Class<? extends Protocol> protocol) {
  105. if (isValidProtocol(protocol))
  106. importConf.addProtocolClass(protocol);
  107. else
  108. return false;
  109. return true;
  110. }
  111. /**
  112. * Removes protocol from the model
  113. *
  114. * @param protocol
  115. * protocol to be removed
  116. */
  117. public void removeProtocol(Class<? extends Protocol> protocol) {
  118. importConf.removeProtocolClass(protocol);
  119. }
  120. /**
  121. * Returns the available Protocols of the model
  122. *
  123. * @return available protocols
  124. */
  125. public LinkedList<Class<? extends Protocol>> getProtocols() {
  126. return importConf.getProtocolClasses();
  127. }
  128. /**
  129. * Returns true if it is a Valid Protocol, false if not
  130. *
  131. * @param protocol
  132. * protocol to be checked
  133. * @return true if it is a valid protocol
  134. */
  135. public boolean isValidProtocol(Class<? extends Protocol> protocol) {
  136. try {
  137. /**
  138. * Protocol to be tested
  139. */
  140. Protocol p = protocol.newInstance();
  141. // Empty constructor required, to create new instance
  142. if (p == null)
  143. throw new Exception("Protocol required an empty constructor");
  144. // Name shall not be null or empty string
  145. if (p.getName() == null || p.getName() == "")
  146. throw new Exception(
  147. "Protocol name shall not be null or empty string.");
  148. if (p.getRoles() == null || p.getRoles().length == 0)
  149. throw new Exception("Roles shall not be null or empty");
  150. // Number of roles have to match
  151. if (p.getNumberOfRoles() != p.getRoles().length)
  152. throw new Exception(
  153. "getNumberOfRoles() does not match getRoles().length");
  154. } catch (Exception e) {
  155. return false;
  156. }
  157. return true;
  158. }
  159. /**
  160. * Adds new Connection to the model
  161. *
  162. * @param connection
  163. * Connection to be added
  164. * @return true if it was added
  165. */
  166. public boolean addConnection(Class<? extends Connection> connection) {
  167. if (isValidConnection(connection))
  168. importConf.addConnectionClass(connection);
  169. else
  170. return false;
  171. return true;
  172. }
  173. /**
  174. * Removes Connection from the model
  175. *
  176. * @param connection
  177. * Connection to be removed
  178. */
  179. public void removeConnection(Class<? extends Connection> connection) {
  180. importConf.removeConnectionClass(connection);
  181. }
  182. /**
  183. * Returns the available Connections of the model
  184. *
  185. * @return available Connections
  186. */
  187. public LinkedList<Class<? extends Connection>> getConnections() {
  188. return importConf.getConnectionClasses();
  189. }
  190. /**
  191. * Returns true if it is a Valid Connection, false if not
  192. *
  193. * @param connection
  194. * Connection to be checked
  195. * @return true if it is a valid Connection
  196. */
  197. public boolean isValidConnection(Class<? extends Connection> connection) {
  198. try {
  199. /**
  200. * Connection to be tested
  201. */
  202. Connection p = connection.newInstance();
  203. // Empty constructor required, to create new instance
  204. if (p == null)
  205. throw new Exception("Connection required an empty constructor");
  206. // Name shall not be null or empty string
  207. if (p.getName() == null || p.getName() == "")
  208. throw new Exception(
  209. "Connection name shall not be null or empty string.");
  210. } catch (Exception e) {
  211. return false;
  212. }
  213. return true;
  214. }
  215. /**
  216. * Adds new SmartDevice to the model
  217. *
  218. * @param smartDevice
  219. * SmartDevice to be added
  220. * @return true if it was added
  221. */
  222. public boolean addSmartDevice(Class<? extends SmartDevice> smartDevice) {
  223. if (isValidSmartDevice(smartDevice))
  224. importConf.addSmartDeviceClass(smartDevice);
  225. else
  226. return false;
  227. return true;
  228. }
  229. /**
  230. * Removes SmartDevice from the model
  231. *
  232. * @param smartDevice
  233. * SmartDevice to be removed
  234. */
  235. public void removeSmartDevice(Class<? extends SmartDevice> smartDevice) {
  236. importConf.removeSmartDeviceClass(smartDevice);
  237. }
  238. /**
  239. * Returns the available SmartDevices of the model
  240. *
  241. * @return available SmartDevices
  242. */
  243. public LinkedList<Class<? extends SmartDevice>> getSmartDevices() {
  244. return importConf.getSmartDeviceClasses();
  245. }
  246. /**
  247. * Returns true if it is a Valid SmartDevice, false if not
  248. *
  249. * @param smartDevice
  250. * SmartDevice to be checked
  251. * @return true if it is a valid SmartDevice
  252. */
  253. public boolean isValidSmartDevice(Class<? extends SmartDevice> smartDevice) {
  254. try {
  255. /**
  256. * SmartDevice to be tested
  257. */
  258. SmartDevice p = smartDevice.newInstance();
  259. // Empty constructor required, to create new instance
  260. if (p == null)
  261. throw new Exception("SmartDevice required an empty constructor");
  262. // Name shall not be null or empty string
  263. if (p.getName() == null || p.getName() == "")
  264. throw new Exception(
  265. "SmartDevice name shall not be null or empty string.");
  266. } catch (Exception e) {
  267. return false;
  268. }
  269. return true;
  270. }
  271. /**
  272. * Adds new DistributionHandler to the model
  273. *
  274. * @param distributionHandler DistributionHandler to be added
  275. * @return true if it was added
  276. */
  277. public boolean addDistributionHandler(Class<? extends ProbabilityDistributionHandler> distributionHandler) {
  278. if (isValidDistributionHandler(distributionHandler))
  279. importConf.addDistributionHandlerClass(distributionHandler);
  280. else
  281. return false;
  282. return true;
  283. }
  284. /**
  285. * Removes DistributionHandler from the model
  286. *
  287. * @param distributionHandler
  288. * DistributionHandler to be removed
  289. */
  290. public void removeDistributionHandler(Class<? extends ProbabilityDistributionHandler> distributionHandler) {
  291. importConf.removeDistributionHandlerClass(distributionHandler);
  292. }
  293. /**
  294. * Returns the available DistributionHandler of the model
  295. *
  296. * @return available DistributionHandler
  297. */
  298. public LinkedList<Class<? extends ProbabilityDistributionHandler>> getDistributionHandlers() {
  299. return importConf.getDistributionHandlerClasses();
  300. }
  301. /**
  302. * Returns true if it is a Valid DistributionHandler, false if not
  303. *
  304. * @param distributionHandler
  305. * DistributionHandler to be checked
  306. * @return true if it is a valid DistributionHandler
  307. */
  308. public boolean isValidDistributionHandler(Class<? extends ProbabilityDistributionHandler> distributionHandler) {
  309. try {
  310. /**
  311. * SmartDevice to be tested
  312. */
  313. ProbabilityDistributionHandler p = distributionHandler.newInstance();
  314. // Empty constructor required, to create new instance
  315. if (p == null)
  316. throw new Exception("DistributionHandler required an empty constructor");
  317. // Name shall not be null or empty string
  318. if (p.getSimpleDescription() == null || p.getSimpleDescription() == "")
  319. throw new Exception(
  320. "DistributionHandler name shall not be null or empty string.");
  321. } catch (Exception e) {
  322. return false;
  323. }
  324. return true;
  325. }
  326. /**
  327. * Imports the given .java File, compiles it and returns the compiled class
  328. *
  329. * @param javaFile File(path)
  330. * @return Class which was compiled
  331. * @throws ClassImportException if an problem occurred during import
  332. */
  333. public static Class<?> importJavaClass(File javaFile) throws ClassImportException{
  334. /**
  335. * Package name
  336. */
  337. String packageName = getJavaPackageName(javaFile);
  338. if (packageName == null) {
  339. // if package null - try default package
  340. packageName = "";
  341. }
  342. /**
  343. * Name of the Class. File name "ClassName.java"
  344. */
  345. String className = javaFile.getName().substring(0,
  346. javaFile.getName().lastIndexOf('.'));
  347. /**
  348. * Compiler, to compile the File
  349. */
  350. JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
  351. compiler.run(null, null, null, javaFile.getPath());
  352. /**
  353. * Root File of the imported Protocol (if the Java File is inside a package)
  354. */
  355. File root = javaFile.getParentFile();
  356. if(!packageName.isEmpty()){
  357. /**
  358. * Array of all parent package names e.g. "pack1.pack2.pack3" -> {"pack1","pack2","pack3"}
  359. */
  360. String[] packageFolders = packageName.split("\\.");
  361. if(packageFolders.length<2){
  362. //A single level
  363. if(root.getName().compareTo(packageName)!=0){
  364. throw new ClassImportException("Invalid ParentFolderName: Expected \""+packageName+"\" but was \""+root.getName()+"\"");
  365. }
  366. root = root.getParentFile();
  367. }else{
  368. for(int i = packageFolders.length-1;i>=0;i--){
  369. if(root.getName().compareTo(packageFolders[i])!=0){
  370. throw new ClassImportException("Invalid ParentFolderName at level "+i+": Expected \""+packageFolders[i]+"\" but was \""+root.getName()+"\"");
  371. }
  372. root = root.getParentFile();
  373. }
  374. }
  375. }
  376. /**
  377. * ClassLoader to load the compiled class, given it's root
  378. */
  379. URLClassLoader classLoader;
  380. try {
  381. classLoader = URLClassLoader
  382. .newInstance(new URL[] { root.toURI().toURL() });
  383. } catch (MalformedURLException e1) {
  384. throw new ClassImportException("Invalid URL/File at the root of the imported Class");
  385. }
  386. // If packageName is not empty, and dot is required: e.g. "packagePath.ClassName"
  387. if (!packageName.isEmpty())
  388. packageName += ".";
  389. /**
  390. * Imported Class
  391. */
  392. Class<?> cls = null;
  393. try{
  394. cls = Class.forName(packageName + className, true, classLoader);
  395. }catch(ClassNotFoundException e){
  396. try{
  397. //Second try to load class
  398. cls = classLoader.loadClass(packageName + className);
  399. }catch(ClassNotFoundException e2){
  400. throw new ClassImportException("Class not found: "+packageName+className);
  401. }
  402. }
  403. return cls;
  404. }
  405. /**
  406. * Returns the package path of a given Java File. Returns null if the
  407. * exception occurred and returns an empty string if no package declaration
  408. * was found. Package declaration should be in a single line.
  409. *
  410. * @param javaFile
  411. * File to be searched for the package path
  412. * @return PackagePath of the JavaFile, EmptyString, if no declaration was
  413. * found, null on errors
  414. */
  415. public static String getJavaPackageName(File javaFile) {
  416. /**
  417. * If javaFile null / non existent -> return null
  418. */
  419. if(javaFile==null||!javaFile.exists())
  420. return null;
  421. /**
  422. * Package name
  423. */
  424. String packageName = "";
  425. /**
  426. * Reader to read the java File
  427. */
  428. BufferedReader reader = null;
  429. try {
  430. reader = new BufferedReader(new FileReader(javaFile.getPath()));
  431. /**
  432. * Current Line which is investigated
  433. */
  434. String currentLine = reader.readLine();
  435. // Search each line until a valid package is found
  436. while (currentLine != null) {
  437. currentLine = currentLine.trim();
  438. if (!currentLine.isEmpty()) {
  439. //Check if line begins with package
  440. if (currentLine.length() >= 7 && currentLine.startsWith("package")) {
  441. packageName = currentLine.substring(8, currentLine.length() - 1);
  442. }
  443. }
  444. if (packageName.isEmpty()) {
  445. currentLine = reader.readLine();
  446. } else {
  447. currentLine = null;
  448. }
  449. }
  450. } catch (Exception e) {
  451. return null;
  452. } finally {
  453. try {
  454. if(reader != null)
  455. reader.close();
  456. } catch (IOException e) {
  457. }
  458. }
  459. // Remove whitespace in cases like "import test.package ;"
  460. packageName = packageName.trim();
  461. return packageName;
  462. }
  463. }