blob: 0734c920ea191e70adba751bc65744c2adf9df96 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009 Mia-Software.
* 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:
* Sebastien Minguet (Mia-Software) - initial API and implementation
* Frederic Madiot (Mia-Software) - initial API and implementation
* Fabien Giquel (Mia-Software) - initial API and implementation
* Gabriel Barbier (Mia-Software) - initial API and implementation
* Erwan Breton (Sodifrance) - initial API and implementation
* Romain Dervaux (Mia-Software) - initial API and implementation
* Nicolas Bros (Mia-Software) - Bug 335003 - [Discoverer] : Existing Discoverers Refactoring based on new framework
*******************************************************************************/
package org.eclipse.modisco.java.discoverer.internal.io.java;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.gmt.modisco.infra.common.core.logging.MoDiscoLogger;
import org.eclipse.gmt.modisco.java.Model;
import org.eclipse.gmt.modisco.java.emf.JavaFactory;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.modisco.java.discoverer.internal.IModelReader;
import org.eclipse.modisco.java.discoverer.internal.JavaActivator;
import org.eclipse.modisco.java.discoverer.internal.Messages;
import org.eclipse.modisco.java.discoverer.internal.io.java.binding.BindingManager;
import org.eclipse.modisco.java.discoverer.internal.io.library.LibraryReader;
import org.eclipse.modisco.kdm.source.extension.discovery.ISourceRegionNotifier;
/**
* A {@code JavaReader} reads the contents of .java files and builds the
* corresponding Java model.
* <p>
* As source, a {@code JavaReader} accepts {@link IJavaProject Java projects},
* single .java {@link ICompilationUnit files}, and .class {@link IClassFile
* files} with source attached.
* </p>
* <p>
* It uses JDT model construction. It is dedicated to J2SE5 (JDK 5 ~ JLS 3).
* </p>
* Using a more recent JLS release may require a new metamodel definition, since
* some new meta-objects may be required.
* <p>
* This reader does not work outside of an eclipse workspace context :
* javaProject should reference a java project of an alive Eclipse Workspace!
* (cf https://bugs.eclipse.org/bugs/show_bug.cgi?id=87852)
* </p>
*
* @see LibraryReader
* @see ASTParser
*/
public class JavaReader implements IModelReader {
private static final int FILE_BUFFER_SIZE = 100;
private boolean incremental = false;
private boolean deepAnalysis = true;
private final JavaFactory factory;
private Model resultModel;
private BindingManager globalBindings;
private Map<String, Object> options;
private String excludedElementsRegEx;
private String includedElementsRegEx;
private ISourceRegionNotifier<?> abstractRegionDiscoverer;
private boolean logJavaAnalysisWarnings;
/**
* Constructs a {@code JavaReader}.
*
* @param factory
* the EMF factory
* @param options
* the options for this reader (may be <code>null</code>)
* @param abstractRegionDiscoverer
* a abstractRegionDiscoverer to be notified of the compilation
* unit visits
*/
public JavaReader(final JavaFactory factory, final Map<String, Object> options,
final ISourceRegionNotifier<?> abstractRegionDiscoverer) {
this.factory = factory;
this.abstractRegionDiscoverer = abstractRegionDiscoverer;
if (options != null) {
setOptions(options);
} else {
setOptions(new HashMap<String, Object>());
}
}
public void setDeepAnalysis(final boolean deepAnalysis) {
this.deepAnalysis = deepAnalysis;
}
public boolean isDeepAnalysis() {
return this.deepAnalysis;
}
public void setIncludedElementsRegEx(final String includedElementsRegEx) {
this.includedElementsRegEx = includedElementsRegEx;
}
public String getIncludedElementsRegEx() {
return this.includedElementsRegEx;
}
public void setExcludedElementsRegEx(final String excludedElementsRegEx) {
this.excludedElementsRegEx = excludedElementsRegEx;
}
public String getExcludedElementsRegEx() {
return this.excludedElementsRegEx;
}
public void setIncremental(final boolean incremental) {
this.incremental = incremental;
}
public boolean isIncremental() {
return this.incremental;
}
public void readModel(final Object source, final Model resultModel1,
final IProgressMonitor monitor) {
readModel(source, resultModel1, getBindingManager(), monitor);
}
public void readModel(final Object source, final Model resultModel1,
final BindingManager bindingManager, final IProgressMonitor monitor) {
if (source == null) {
return;
}
setResultModel(resultModel1);
setGlobalBindings(bindingManager);
if (this.incremental) {
getGlobalBindings().enableIncrementalDiscovering(getResultModel());
} else {
getGlobalBindings().disableIncrementalDiscovering();
}
JDTVisitorUtils.initializePrimitiveTypes(this.factory, resultModel1, getGlobalBindings());
try {
if (source instanceof IJavaProject) {
IJavaProject javaProject = (IJavaProject) source;
if (resultModel1.getName() == null || resultModel1.getName().length() == 0) {
resultModel1.setName(javaProject.getElementName());
}
IPackageFragment[] packageFolder = javaProject.getPackageFragments();
// loop on CompilationUnit-s
for (IPackageFragment parent : packageFolder) {
// test if package has compilations units and has not been
// excluded
if (parent.getCompilationUnits().length > 0 && !ignorePackage(parent)) {
// report some feedback
monitor.subTask(Messages.JavaReader_discoveringTask
+ parent.getElementName());
// parse package
parsePackage(javaProject, resultModel1, parent, monitor);
}
if (monitor.isCanceled()) {
return;
}
}
} else if (source instanceof ITypeRoot) {
parseTypeRoot((ITypeRoot) source);
} else {
throw new IllegalArgumentException(
"Java reader can not handle source object : " + source.toString()); //$NON-NLS-1$
}
} catch (Exception e) {
MoDiscoLogger.logError(e, JavaActivator.getDefault());
}
}
/**
* Indicate if a element is excluded from analyse (user filter choice)
*
* @param qualifiedName
* the qualified name of the element
* @return
*/
protected boolean isElementExcluded(final String qualifiedName) {
if (this.excludedElementsRegEx != null && this.excludedElementsRegEx.length() > 0) {
return qualifiedName.matches(this.excludedElementsRegEx);
}
return false;
}
/**
* Indicate if a qualified name is included in analyse (user filter choice)
*
* @param qualifiedName
* the qualified name of the element
* @return
*/
protected boolean isElementIncluded(final String qualifiedName) {
if (this.includedElementsRegEx != null && this.includedElementsRegEx.length() > 0) {
return qualifiedName.matches(this.includedElementsRegEx);
}
return true; // no reg ex inclusion : all elements are included
}
/**
* Computes if a package does not contain any types to be analyzed
* (according to user filter choice)
*
* @param aPackage
* @return
* @throws JavaModelException
*/
protected boolean ignorePackage(final IPackageFragment aPackage) throws JavaModelException {
if (isElementExcluded(aPackage.getElementName())) {
return true;
}
if (isElementIncluded(aPackage.getElementName())) {
return false;
}
// Package is not explicitly included but maybe one of its owned types
// has to be analyzed
boolean noSubElementToAnalyse = true;
for (ICompilationUnit cu : aPackage.getCompilationUnits()) {
for (IType t : cu.getTypes()) {
String qualifedName = t.getFullyQualifiedName();
if (!isElementExcluded(qualifedName) && isElementIncluded(qualifedName)) {
noSubElementToAnalyse = false;
}
}
}
// no need of recursion : aPackage does not own sub packages
return noSubElementToAnalyse;
}
protected void parseTypeRoot(final ITypeRoot source) {
org.eclipse.jdt.core.dom.CompilationUnit parsedCompilationUnit = parseCompilationUnit(source);
String fileContent = null;
String filePath = null;
try {
if (source instanceof ICompilationUnit) {
IFile theIFile = ResourcesPlugin.getWorkspace().getRoot()
.getFile(parsedCompilationUnit.getJavaElement().getPath());
// getContent(IFile) is faster than ICompilationUnit.getSource()
fileContent = getContent(theIFile).toString();
IProject project = source.getJavaProject().getProject();
filePath = getRelativePath(project, parsedCompilationUnit);
} else {
// IJavaElement.CLASS_FILE
fileContent = LibraryReader.getFileContent((IClassFile) source);
filePath = LibraryReader.getPath((IClassFile) source);
}
visitCompilationUnit(getResultModel(), parsedCompilationUnit, filePath, fileContent);
} catch (Exception e) {
MoDiscoLogger.logError(e, JavaActivator.getDefault());
}
}
protected void parsePackage(final IJavaProject javaProject, final Model resultModel1,
final IPackageFragment parent, final IProgressMonitor monitor)
throws JavaModelException {
boolean analyseAllPackage = !isElementExcluded(parent.getElementName())
&& isElementIncluded(parent.getElementName());
ICompilationUnit[] children = parent.getCompilationUnits();
for (ICompilationUnit cu : children) {
// iterate on each type of each CU and check if one is excluded
boolean isExcluded = false;
for (IType t : cu.getTypes()) {
if (isElementExcluded(t.getFullyQualifiedName())
|| !(analyseAllPackage || isElementIncluded(t.getFullyQualifiedName()))) {
isExcluded = true;
break;
}
}
if (!isExcluded) {
parseTypeRoot(cu);
}
if (monitor.isCanceled()) {
return;
}
}
if (this.incremental) {
reset();
}
}
protected void visitCompilationUnit(final Model resultModel1,
final org.eclipse.jdt.core.dom.CompilationUnit parsedCompilationUnit,
final String filePath, final String fileContent) {
JDTVisitor jdtVisitor = new JDTVisitor(this.factory, resultModel1, getGlobalBindings(),
filePath, fileContent, getGlobalBindings().isIncrementalDiscovering(),
this.deepAnalysis, this.logJavaAnalysisWarnings);
parsedCompilationUnit.accept(jdtVisitor);
if (this.abstractRegionDiscoverer != null) {
for (ASTNode key : jdtVisitor.getBijectiveMap().getKeys()) {
int startPosition = key.getStartPosition();
int endPosition = startPosition + key.getLength();
int startLine = parsedCompilationUnit.getLineNumber(startPosition);
int endLine = parsedCompilationUnit.getLineNumber(endPosition);
this.abstractRegionDiscoverer
.notifySourceRegionVisited(filePath, startPosition, endPosition, startLine,
endLine, jdtVisitor.getBijectiveMap().getValue(key));
}
}
}
protected static CompilationUnit parseCompilationUnit(final ITypeRoot source) {
// Code parsing : here is indicated the version of JDK (~JLS) to
// consider, see Class comments
ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setResolveBindings(true);
parser.setSource(source);
CompilationUnit parsedCompilationUnit = (CompilationUnit) parser.createAST(null);
return parsedCompilationUnit;
}
protected void resolveMethodRedefinition(final Model resultModel1) {
MethodRedefinitionManager.resolveMethodRedefinitions(resultModel1, this.factory);
}
protected void finalResolveBindings(final Model resultModel1) {
getGlobalBindings().resolveBindings(resultModel1);
}
public static String getRelativePath(final IProject aProject,
final CompilationUnit parsedCompilationUnit) {
IPath projectpath = aProject.getFullPath();
IPath filepath = parsedCompilationUnit.getJavaElement().getPath();
// we want path relative to project directory
if (projectpath.isPrefixOf(filepath)) {
filepath = filepath.removeFirstSegments(projectpath.segmentCount());
}
String filePathString = filepath.toOSString();
if (!filePathString.startsWith(java.io.File.separator)) {
filePathString = java.io.File.separator + filePathString;
}
return filePathString;
}
public static StringBuilder getContent(final IFile anIFile) throws CoreException, IOException {
InputStream is = anIFile.getContents();
StringBuilder cuText = new StringBuilder();
Reader r = new InputStreamReader(is);
char[] chars = new char[JavaReader.FILE_BUFFER_SIZE];
int read;
while ((read = r.read(chars)) != -1) {
if (read == JavaReader.FILE_BUFFER_SIZE) {
cuText.append(chars);
} else {
cuText.append(chars, 0, read);
}
}
is.close();
return cuText;
}
protected void reset() {
getGlobalBindings().resolveBindings(getResultModel());
setGlobalBindings(getBindingManager());
}
protected BindingManager getBindingManager() {
BindingManager bindingManager = new BindingManager(this.factory);
if (this.incremental) {
bindingManager.enableIncrementalDiscovering(getResultModel());
}
return bindingManager;
}
public void terminate(final IProgressMonitor monitor) {
monitor.subTask(Messages.JavaReader_bindingsTask);
finalResolveBindings(getResultModel());
monitor.subTask(Messages.JavaReader_redefinitionsTask);
resolveMethodRedefinition(getResultModel());
}
protected void setResultModel(final Model resultModel) {
this.resultModel = resultModel;
}
public Model getResultModel() {
return this.resultModel;
}
public void setGlobalBindings(final BindingManager globalBindings) {
this.globalBindings = globalBindings;
}
public BindingManager getGlobalBindings() {
return this.globalBindings;
}
public void setOptions(final Map<String, Object> options) {
this.options = options;
}
public Map<String, Object> getOptions() {
return this.options;
}
public void setAbstractRegionDiscoverer(
final ISourceRegionNotifier<?> abstractRegionDiscoverer) {
this.abstractRegionDiscoverer = abstractRegionDiscoverer;
}
}