JavaImport.java
/* $Id$
*****************************************************************************
* Copyright (c) 2009-2013 Contributors - see below
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* thn
*****************************************************************************
*
* Some portions of this file was previously release using the BSD License:
*/
// Copyright (c) 1996-2008 The Regents of the University of California. All
// Rights Reserved. Permission to use, copy, modify, and distribute this
// software and its documentation without fee, and without a written
// agreement is hereby granted, provided that the above copyright notice
// and this paragraph appear in all copies. This software program and
// documentation are copyrighted by The Regents of the University of
// California. The software program and documentation are supplied "AS
// IS", without any accompanying services from The Regents. The Regents
// does not warrant that the operation of the program will be
// uninterrupted or error-free. The end-user understands that the program
// was developed for research purposes and is advised not to rely
// exclusively on the program for any reason. IN NO EVENT SHALL THE
// UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
// SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
// PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
// CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
// UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
package org.argouml.language.java.reveng;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.antlr.runtime.ANTLRReaderStream;
import org.antlr.runtime.CommonTokenStream;
import org.argouml.i18n.Translator;
import org.argouml.kernel.Project;
import org.argouml.language.java.JavaModuleGlobals;
import org.argouml.model.IllegalModelElementConnectionException;
import org.argouml.moduleloader.ModuleInterface;
import org.argouml.profile.Profile;
import org.argouml.taskmgmt.ProgressMonitor;
import org.argouml.uml.reveng.FileImportUtils;
import org.argouml.uml.reveng.ImportClassLoader;
import org.argouml.uml.reveng.ImportInterface;
import org.argouml.uml.reveng.ImportSettings;
import org.argouml.uml.reveng.ImporterManager;
import org.argouml.uml.reveng.SettingsTypes;
import org.argouml.uml.util.ModelUtil;
import org.argouml.util.SuffixFilter;
/**
* This is the main class for Java reverse engineering. It's based on the Antlr
* Java example.
*
* @author Andreas Rueckert <a_rueckert@gmx.net>
*/
public class JavaImport implements ModuleInterface, ImportInterface {
/** Logger. */
private static final Logger LOG =
Logger.getLogger(JavaImport.class.getName());
/**
* Java profile model.
*/
private Profile javaProfile = null;
/**
* New model elements that were added
*/
private Collection<Object> newElements;
/*
* @see org.argouml.uml.reveng.ImportInterface#parseFiles(org.argouml.kernel.Project,
* java.util.Collection, org.argouml.uml.reveng.ImportSettings,
* org.argouml.application.api.ProgressMonitor)
*/
public Collection parseFiles(Project p, Collection<File> files,
ImportSettings settings, ProgressMonitor monitor)
throws ImportException {
JavaImportSettings.getInstance().saveSettings();
updateImportClassloader();
newElements = new HashSet<Object>();
monitor.updateMainTask(Translator.localize("dialog.import.pass1"));
// get the Java profile from project, if available
javaProfile = getJavaProfile(p);
try {
if ((settings.getImportLevel()
== ImportSettings.DETAIL_CLASSIFIER_FEATURE)
|| settings.getImportLevel() == ImportSettings.DETAIL_FULL) {
monitor.setMaximumProgress(files.size() * 2);
doImportPass(p, files, settings, monitor, 0, 0);
if (!monitor.isCanceled()) {
monitor.updateMainTask(Translator
.localize("dialog.import.pass2"));
doImportPass(p, files, settings, monitor, files.size(), 1);
}
} else {
monitor.setMaximumProgress(files.size() * 2);
doImportPass(p, files, settings, monitor, 0, 0);
}
ModelUtil.generatePackageDependencies(p);
} catch (IllegalModelElementConnectionException e) {
} finally {
//this prevents parse problems to be displayed, so I disabled it:
// --thn
//monitor.close();
}
return newElements;
}
private void doImportPass(Project p, Collection<File> files,
ImportSettings settings, ProgressMonitor monitor, int startCount,
int pass) {
int count = startCount;
for (File file : files) {
if (monitor.isCanceled()) {
monitor.updateSubTask(Translator
.localize("dialog.import.cancelled"));
return;
}
try {
parseFile(p, file, settings, pass);
} catch (Exception e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new java.io.PrintWriter(sw);
e.printStackTrace(pw);
monitor.notifyMessage(
Translator.localize(
"dialog.title.import-problems"), //$NON-NLS-1$
Translator.localize("label.import-problems"), //$NON-NLS-1$
sw.toString());
if (monitor.isCanceled()) {
break;
}
}
monitor.updateProgress(count++);
monitor.updateSubTask(Translator.localize(
"dialog.import.parsingAction",
new Object[] {
file.getAbsolutePath()
}));
}
}
/**
* Do a single import pass of a single file.
*
* @param p the project
* @param f the source file
* @param settings the user provided import settings
* @param pass current import pass - 0 = single pass, 1 = pass 1 of 2, 2 =
* pass 2 of 2
*/
private void parseFile(Project p, File f, ImportSettings settings, int pass)
throws ImportException {
try {
// Create a scanner that reads from the input stream
String encoding = settings.getInputSourceEncoding();
FileInputStream in = new FileInputStream(f);
InputStreamReader isr;
try {
isr = new InputStreamReader(in, encoding);
} catch (UnsupportedEncodingException e) {
// fall back to default encoding
isr = new InputStreamReader(in);
}
JavaLexer lexer = new JavaLexer(new ANTLRReaderStream(isr));
// Create a parser that reads from the scanner
JavaParser parser = new JavaParser(new CommonTokenStream(lexer));
// Pass == 0 means single pass recognition
int parserMode = JavaParser.MODE_IMPORT_PASS1
| JavaParser.MODE_IMPORT_PASS2;
if (pass == 0) {
parserMode = JavaParser.MODE_IMPORT_PASS1;
} else if (pass == 1) {
parserMode = JavaParser.MODE_IMPORT_PASS2;
}
parser.setParserMode(parserMode);
// Create a modeller for the parser
Modeller modeller = new Modeller(
p.getUserDefinedModelList().get(0), javaProfile,
JavaImportSettings.getInstance().isAttributeSelected(),
JavaImportSettings.getInstance().isDatatypeSelected(), f
.getName());
// Print the name of the current file, so we can associate
// exceptions to the file.
LOG.info("Parsing " + f.getAbsolutePath());
// Calculate the import level
int level = 0;
int importlevel = settings.getImportLevel();
if (importlevel == ImportSettings.DETAIL_CLASSIFIER_FEATURE) {
level = 1;
} else if (importlevel == ImportSettings.DETAIL_FULL) {
// full level only needed for the second pass
level = (pass == 0) ? 0 : 2;
}
modeller.setAttribute("level", Integer.valueOf(level));
try {
// start parsing at the compilationUnit rule
parser.compilationUnit(modeller, lexer);
} catch (Exception e) {
String errorString = buildErrorString(f);
LOG.log(Level.SEVERE,
e.getClass().getName() + errorString,
e);
throw new ImportException(errorString, e);
} finally {
newElements.addAll(modeller.getNewElements());
in.close();
}
} catch (IOException e) {
throw new ImportException(buildErrorString(f), e);
}
}
private String buildErrorString(File f) {
String path = "";
try {
path = f.getCanonicalPath();
} catch (IOException e) {
// Just ignore - we'll use the simple file name
LOG.log(Level.FINEST,
"Cannot get the Canonical Path, using name without path",
e);
}
return "Exception in file: " + path + " " + f.getName();
}
/*
* @see org.argouml.uml.reveng.ImportInterface#getSuffixFilters()
*/
public SuffixFilter[] getSuffixFilters() {
SuffixFilter[] result = {
new SuffixFilter("java",
Translator.localize("java.filefilter.java")),
};
return result;
}
/*
* @see org.argouml.uml.reveng.ImportInterface#isParseable(java.io.File)
*/
public boolean isParseable(File file) {
return FileImportUtils.matchesSuffix(file, getSuffixFilters());
}
/*
* @see org.argouml.moduleloader.ModuleInterface#getName()
*/
public String getName() {
return "Java";
}
/*
* @see org.argouml.moduleloader.ModuleInterface#getInfo(int)
*/
public String getInfo(int type) {
switch (type) {
case DESCRIPTION:
return "Java import from Java files.";
case AUTHOR:
return JavaModuleGlobals.MODULE_AUTHOR;
case VERSION:
return JavaModuleGlobals.MODULE_VERSION;
case ModuleInterface.DOWNLOADSITE:
return JavaModuleGlobals.MODULE_DOWNLOADSITE;
default:
return null;
}
}
/*
* @see org.argouml.moduleloader.ModuleInterface#disable()
*/
public boolean disable() {
// We are permanently enabled
return false;
}
/*
* @see org.argouml.moduleloader.ModuleInterface#enable()
*/
public boolean enable() {
ImporterManager.getInstance().addImporter(this);
return true;
}
/*
* @see org.argouml.uml.reveng.ImportInterface#getImportSettings()
*/
public List<SettingsTypes.Setting> getImportSettings() {
return JavaImportSettings.getInstance().getImportSettings();
}
private void updateImportClassloader() {
List<String> pathList = JavaImportSettings.getInstance().getPathList();
URL[] urls = new URL[pathList.size()];
int i = 0;
for (String path : pathList) {
try {
urls[i++] = new File(path).toURI().toURL();
} catch (MalformedURLException e) {
LOG.severe("Bad path in classpath " + path);
}
}
try {
ImportClassLoader.getInstance(urls);
ImportClassLoader.getInstance().saveUserPath();
} catch (MalformedURLException e) {
LOG.log(Level.FINEST, "Bad path in classpaths", e);
}
}
/**
* Get the Java profile from project, if available.
*
* @param p the project
* @return the Java profile
*/
private Profile getJavaProfile(Project p) {
for (Profile profile : p.getProfileConfiguration().getProfiles()) {
if ("Java".equals(profile.getDisplayName())) {
return profile;
}
}
return null;
}
}