JavaImport.java

  1. /* $Id$
  2.  *****************************************************************************
  3.  * Copyright (c) 2009-2013 Contributors - see below
  4.  * All rights reserved. This program and the accompanying materials
  5.  * are made available under the terms of the Eclipse Public License v1.0
  6.  * which accompanies this distribution, and is available at
  7.  * http://www.eclipse.org/legal/epl-v10.html
  8.  *
  9.  * Contributors:
  10.  *    thn
  11.  *****************************************************************************
  12.  *
  13.  * Some portions of this file was previously release using the BSD License:
  14.  */

  15. // Copyright (c) 1996-2008 The Regents of the University of California. All
  16. // Rights Reserved. Permission to use, copy, modify, and distribute this
  17. // software and its documentation without fee, and without a written
  18. // agreement is hereby granted, provided that the above copyright notice
  19. // and this paragraph appear in all copies.  This software program and
  20. // documentation are copyrighted by The Regents of the University of
  21. // California. The software program and documentation are supplied "AS
  22. // IS", without any accompanying services from The Regents. The Regents
  23. // does not warrant that the operation of the program will be
  24. // uninterrupted or error-free. The end-user understands that the program
  25. // was developed for research purposes and is advised not to rely
  26. // exclusively on the program for any reason.  IN NO EVENT SHALL THE
  27. // UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
  28. // SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
  29. // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
  30. // THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
  31. // SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
  32. // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  33. // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
  34. // PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
  35. // CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
  36. // UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

  37. package org.argouml.language.java.reveng;

  38. import java.io.File;
  39. import java.io.FileInputStream;
  40. import java.io.IOException;
  41. import java.io.InputStreamReader;
  42. import java.io.PrintWriter;
  43. import java.io.StringWriter;
  44. import java.io.UnsupportedEncodingException;
  45. import java.net.MalformedURLException;
  46. import java.net.URL;
  47. import java.util.Collection;
  48. import java.util.HashSet;
  49. import java.util.List;
  50. import java.util.logging.Level;
  51. import java.util.logging.Logger;

  52. import org.antlr.runtime.ANTLRReaderStream;
  53. import org.antlr.runtime.CommonTokenStream;
  54. import org.argouml.i18n.Translator;
  55. import org.argouml.kernel.Project;
  56. import org.argouml.language.java.JavaModuleGlobals;
  57. import org.argouml.model.IllegalModelElementConnectionException;
  58. import org.argouml.moduleloader.ModuleInterface;
  59. import org.argouml.profile.Profile;
  60. import org.argouml.taskmgmt.ProgressMonitor;
  61. import org.argouml.uml.reveng.FileImportUtils;
  62. import org.argouml.uml.reveng.ImportClassLoader;
  63. import org.argouml.uml.reveng.ImportInterface;
  64. import org.argouml.uml.reveng.ImportSettings;
  65. import org.argouml.uml.reveng.ImporterManager;
  66. import org.argouml.uml.reveng.SettingsTypes;
  67. import org.argouml.uml.util.ModelUtil;
  68. import org.argouml.util.SuffixFilter;

  69. /**
  70.  * This is the main class for Java reverse engineering. It's based on the Antlr
  71.  * Java example.
  72.  *
  73.  * @author Andreas Rueckert <a_rueckert@gmx.net>
  74.  */
  75. public class JavaImport implements ModuleInterface, ImportInterface {

  76.     /** Logger. */
  77.     private static final Logger LOG =
  78.         Logger.getLogger(JavaImport.class.getName());

  79.     /**
  80.      * Java profile model.
  81.      */
  82.     private Profile javaProfile = null;

  83.     /**
  84.      * New model elements that were added
  85.      */
  86.     private Collection<Object> newElements;

  87.     /*
  88.      * @see org.argouml.uml.reveng.ImportInterface#parseFiles(org.argouml.kernel.Project,
  89.      *      java.util.Collection, org.argouml.uml.reveng.ImportSettings,
  90.      *      org.argouml.application.api.ProgressMonitor)
  91.      */
  92.     public Collection parseFiles(Project p, Collection<File> files,
  93.             ImportSettings settings, ProgressMonitor monitor)
  94.         throws ImportException {

  95.         JavaImportSettings.getInstance().saveSettings();
  96.         updateImportClassloader();
  97.         newElements = new HashSet<Object>();
  98.         monitor.updateMainTask(Translator.localize("dialog.import.pass1"));

  99.         // get the Java profile from project, if available
  100.         javaProfile = getJavaProfile(p);

  101.         try {
  102.             if ((settings.getImportLevel()
  103.                     == ImportSettings.DETAIL_CLASSIFIER_FEATURE)
  104.                 || settings.getImportLevel() == ImportSettings.DETAIL_FULL) {
  105.                 monitor.setMaximumProgress(files.size() * 2);
  106.                 doImportPass(p, files, settings, monitor, 0, 0);
  107.                 if (!monitor.isCanceled()) {
  108.                     monitor.updateMainTask(Translator
  109.                             .localize("dialog.import.pass2"));
  110.                     doImportPass(p, files, settings, monitor, files.size(), 1);
  111.                 }
  112.             } else {
  113.                 monitor.setMaximumProgress(files.size() * 2);
  114.                 doImportPass(p, files, settings, monitor, 0, 0);
  115.             }
  116.            
  117.             ModelUtil.generatePackageDependencies(p);
  118.         } catch (IllegalModelElementConnectionException e) {
  119.         } finally {
  120.             //this prevents parse problems to be displayed, so I disabled it:
  121.             // --thn
  122.             //monitor.close();
  123.         }
  124.         return newElements;
  125.     }

  126.     private void doImportPass(Project p, Collection<File> files,
  127.             ImportSettings settings, ProgressMonitor monitor, int startCount,
  128.             int pass) {

  129.         int count = startCount;
  130.         for (File file : files) {
  131.             if (monitor.isCanceled()) {
  132.                 monitor.updateSubTask(Translator
  133.                         .localize("dialog.import.cancelled"));
  134.                 return;
  135.             }
  136.             try {
  137.                 parseFile(p, file, settings, pass);
  138.             } catch (Exception e) {
  139.                 StringWriter sw = new StringWriter();
  140.                 PrintWriter pw = new java.io.PrintWriter(sw);
  141.                 e.printStackTrace(pw);
  142.                 monitor.notifyMessage(
  143.                     Translator.localize(
  144.                             "dialog.title.import-problems"), //$NON-NLS-1$
  145.                     Translator.localize("label.import-problems"), //$NON-NLS-1$
  146.                     sw.toString());
  147.                 if (monitor.isCanceled()) {
  148.                     break;
  149.                 }
  150.             }
  151.             monitor.updateProgress(count++);
  152.             monitor.updateSubTask(Translator.localize(
  153.                     "dialog.import.parsingAction",
  154.                     new Object[] {
  155.                         file.getAbsolutePath()
  156.                     }));
  157.         }
  158.     }

  159.     /**
  160.      * Do a single import pass of a single file.
  161.      *
  162.      * @param p the project
  163.      * @param f the source file
  164.      * @param settings the user provided import settings
  165.      * @param pass current import pass - 0 = single pass, 1 = pass 1 of 2, 2 =
  166.      *                pass 2 of 2
  167.      */
  168.     private void parseFile(Project p, File f, ImportSettings settings, int pass)
  169.         throws ImportException {

  170.         try {
  171.             // Create a scanner that reads from the input stream
  172.             String encoding = settings.getInputSourceEncoding();
  173.             FileInputStream in = new FileInputStream(f);
  174.             InputStreamReader isr;
  175.             try {
  176.                 isr = new InputStreamReader(in, encoding);
  177.             } catch (UnsupportedEncodingException e) {
  178.                 // fall back to default encoding
  179.                 isr = new InputStreamReader(in);
  180.             }
  181.             JavaLexer lexer = new JavaLexer(new ANTLRReaderStream(isr));

  182.             // Create a parser that reads from the scanner
  183.             JavaParser parser = new JavaParser(new CommonTokenStream(lexer));

  184.             // Pass == 0 means single pass recognition
  185.             int parserMode = JavaParser.MODE_IMPORT_PASS1
  186.                     | JavaParser.MODE_IMPORT_PASS2;
  187.             if (pass == 0) {
  188.                 parserMode = JavaParser.MODE_IMPORT_PASS1;
  189.             } else if (pass == 1) {
  190.                 parserMode = JavaParser.MODE_IMPORT_PASS2;
  191.             }
  192.             parser.setParserMode(parserMode);

  193.             // Create a modeller for the parser
  194.             Modeller modeller = new Modeller(
  195.                     p.getUserDefinedModelList().get(0), javaProfile,
  196.                     JavaImportSettings.getInstance().isAttributeSelected(),
  197.                     JavaImportSettings.getInstance().isDatatypeSelected(), f
  198.                             .getName());

  199.             // Print the name of the current file, so we can associate
  200.             // exceptions to the file.
  201.             LOG.info("Parsing " + f.getAbsolutePath());

  202.             // Calculate the import level
  203.             int level = 0;
  204.             int importlevel = settings.getImportLevel();
  205.             if (importlevel == ImportSettings.DETAIL_CLASSIFIER_FEATURE) {
  206.                 level = 1;
  207.             } else if (importlevel == ImportSettings.DETAIL_FULL) {
  208.                 // full level only needed for the second pass
  209.                 level = (pass == 0) ? 0 : 2;
  210.             }
  211.             modeller.setAttribute("level", Integer.valueOf(level));

  212.             try {
  213.                 // start parsing at the compilationUnit rule
  214.                 parser.compilationUnit(modeller, lexer);
  215.             } catch (Exception e) {
  216.                 String errorString = buildErrorString(f);
  217.                 LOG.log(Level.SEVERE,
  218.                         e.getClass().getName() + errorString,
  219.                         e);
  220.                 throw new ImportException(errorString, e);
  221.             } finally {
  222.                 newElements.addAll(modeller.getNewElements());
  223.                 in.close();
  224.             }
  225.         } catch (IOException e) {
  226.             throw new ImportException(buildErrorString(f), e);
  227.         }
  228.     }

  229.     private String buildErrorString(File f) {
  230.         String path = "";
  231.         try {
  232.             path = f.getCanonicalPath();
  233.         } catch (IOException e) {
  234.             // Just ignore - we'll use the simple file name
  235.             LOG.log(Level.FINEST,
  236.                     "Cannot get the Canonical Path, using name without path",
  237.                     e);
  238.         }
  239.         return "Exception in file: " + path + " " + f.getName();
  240.     }

  241.     /*
  242.      * @see org.argouml.uml.reveng.ImportInterface#getSuffixFilters()
  243.      */
  244.     public SuffixFilter[] getSuffixFilters() {
  245.         SuffixFilter[] result = {
  246.             new SuffixFilter("java",
  247.                     Translator.localize("java.filefilter.java")),
  248.         };
  249.         return result;
  250.     }

  251.     /*
  252.      * @see org.argouml.uml.reveng.ImportInterface#isParseable(java.io.File)
  253.      */
  254.     public boolean isParseable(File file) {
  255.         return FileImportUtils.matchesSuffix(file, getSuffixFilters());
  256.     }

  257.     /*
  258.      * @see org.argouml.moduleloader.ModuleInterface#getName()
  259.      */
  260.     public String getName() {
  261.         return "Java";
  262.     }

  263.     /*
  264.      * @see org.argouml.moduleloader.ModuleInterface#getInfo(int)
  265.      */
  266.     public String getInfo(int type) {
  267.         switch (type) {
  268.         case DESCRIPTION:
  269.             return "Java import from Java files.";
  270.         case AUTHOR:
  271.             return JavaModuleGlobals.MODULE_AUTHOR;
  272.         case VERSION:
  273.             return JavaModuleGlobals.MODULE_VERSION;
  274.         case ModuleInterface.DOWNLOADSITE:
  275.             return JavaModuleGlobals.MODULE_DOWNLOADSITE;
  276.         default:
  277.             return null;
  278.         }
  279.     }

  280.     /*
  281.      * @see org.argouml.moduleloader.ModuleInterface#disable()
  282.      */
  283.     public boolean disable() {
  284.         // We are permanently enabled
  285.         return false;
  286.     }

  287.     /*
  288.      * @see org.argouml.moduleloader.ModuleInterface#enable()
  289.      */
  290.     public boolean enable() {
  291.         ImporterManager.getInstance().addImporter(this);
  292.         return true;
  293.     }

  294.     /*
  295.      * @see org.argouml.uml.reveng.ImportInterface#getImportSettings()
  296.      */
  297.     public List<SettingsTypes.Setting> getImportSettings() {
  298.         return JavaImportSettings.getInstance().getImportSettings();
  299.     }

  300.     private void updateImportClassloader() {
  301.         List<String> pathList = JavaImportSettings.getInstance().getPathList();
  302.         URL[] urls = new URL[pathList.size()];

  303.         int i = 0;
  304.         for (String path : pathList) {
  305.             try {
  306.                 urls[i++] = new File(path).toURI().toURL();
  307.             } catch (MalformedURLException e) {
  308.                 LOG.severe("Bad path in classpath " + path);
  309.             }
  310.         }

  311.         try {
  312.             ImportClassLoader.getInstance(urls);
  313.             ImportClassLoader.getInstance().saveUserPath();
  314.         } catch (MalformedURLException e) {
  315.             LOG.log(Level.FINEST, "Bad path in classpaths", e);
  316.         }
  317.     }

  318.     /**
  319.      * Get the Java profile from project, if available.
  320.      *
  321.      * @param p the project
  322.      * @return the Java profile
  323.      */
  324.     private Profile getJavaProfile(Project p) {
  325.         for (Profile profile : p.getProfileConfiguration().getProfiles()) {
  326.             if ("Java".equals(profile.getDisplayName())) {
  327.                 return profile;
  328.             }
  329.         }
  330.         return null;
  331.     }
  332. }