Java tutorial
/******************************************************************************* * 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: * Romain Dervaux (Mia-Software) - initial API and implementation *******************************************************************************/ package org.eclipse.modisco.java.discoverer.internal.io.library; import java.util.HashMap; import java.util.Map; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.gmt.modisco.infra.common.core.logging.MoDiscoLogger; import org.eclipse.gmt.modisco.java.Archive; 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.IJavaElement; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; 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.JavaReader; import org.eclipse.modisco.java.discoverer.internal.io.java.MethodRedefinitionManager; import org.eclipse.modisco.java.discoverer.internal.io.java.binding.BindingManager; /** * A {@code LibraryReader} reads the contents of .class files and builds the * corresponding Java model. * <p> * As source, a {@code LibraryReader} accepts {@link IPackageFragmentRoot * libraries} and single .class {@link IClassFile files}. * </p> * <p> * The analysis doesn't go beyond root types and members. Bytecode (inside * methods), local classes & anonymous classes are ignored. * </p> * <p> * The model is built along the Java 1.4 language specification. This means that * all specificities of Java 5 are not handled (Enumerations and annotations are * translated respectively as classes & interfaces, only raw types of * parameterized types are considered, type variables & wildcard types are * translated as basic {@link Object}s, ...). * </p> * <p> * It handles both internal and {@link IPackageFragmentRoot#isExternal() * external} libraries. * </p> * <p> * If sources are * {@link IPackageFragmentRoot#attachSource(IPath, IPath, org.eclipse.core.runtime.IProgressMonitor) * attached} to the library, the use of the * {@link LibraryReaderOptions#USE_SOURCES USE_SOURCES} option will cause the * creation of the model from the sources, relying on a {@code JavaReader}. * </p> * * @see JavaReader */ public class LibraryReader implements IModelReader { /** * the EMF factory. */ private final JavaFactory factory; /** * the resulting model. */ private Model resultModel; /** * the global {@code BindingManager}. */ private BindingManager globalBindings; /** * some options for this reader. */ @SuppressWarnings("unused") private final Map<String, Object> options; /** * a {@code TypeFinder}. */ private TypeFinder typeFinder; /** * indicate if the user wanted to analyse the sources */ private final boolean useSources; /** * Contructs a new {@code LibraryReader} with no options. * * @param factory * the EMF factory * @param library * the library */ public LibraryReader(final JavaFactory factory) { this(factory, new HashMap<String, Object>()); } /** * Contructs a new {@code LibraryReader} with options. * * @param factory * the EMF factory * @param library * the library to analyse * @param options * the {@link LibraryReaderOptions options} of this * {@code LibraryReader} */ public LibraryReader(final JavaFactory factory, final Map<String, Object> options) { this.factory = factory; this.options = options; this.useSources = Boolean.TRUE.equals(options.get(LibraryReaderOptions.USE_SOURCES.toString())); } 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 globalBindings1, final IProgressMonitor monitor) { if (source == null) { return; } this.resultModel = resultModel1; this.globalBindings = globalBindings1; ClassFileParserUtils.initializePrimitiveTypes(this.factory, resultModel1, this.globalBindings); try { if (source instanceof IPackageFragmentRoot) { IPackageFragmentRoot library = (IPackageFragmentRoot) source; if (resultModel1.getName() == null || resultModel1.getName().length() == 0) { resultModel1.setName(library.getElementName()); } this.typeFinder = new TypeFinder(library.getJavaProject()); IJavaElement[] children = library.getChildren(); for (IJavaElement element : children) { IPackageFragment packageFolder = (IPackageFragment) element; if (packageFolder.getClassFiles().length > 0) { // report some feedback monitor.subTask(Messages.LibraryReader_DiscoveringTask + packageFolder.getElementName()); // parse package parsePackage(library, resultModel1, packageFolder, monitor); if (monitor.isCanceled()) { return; } } } } else if (source instanceof IClassFile) { IClassFile cf = (IClassFile) source; this.typeFinder = new TypeFinder(cf.getJavaProject()); parseClassFile(cf); } else { throw new IllegalArgumentException( "Library reader can not handle source object : " + source.toString()); //$NON-NLS-1$ } } catch (Exception e) { MoDiscoLogger.logError(e, JavaActivator.getDefault()); } } protected void parsePackage(final IPackageFragmentRoot library, final Model resultModel1, final IPackageFragment parent, final IProgressMonitor monitor) throws JavaModelException { IClassFile[] children = parent.getClassFiles(); for (IClassFile cf : children) { parseClassFile(cf); if (monitor.isCanceled()) { return; } } } protected void parseClassFile(final IClassFile classFile) { try { IType type = classFile.getType(); // we want only top level types if (type != null && type.exists() && !type.isAnonymous() && !type.isLocal() && !type.isMember()) { String filePath = getPath(classFile); visitClassFile(classFile, filePath); } } catch (Exception e) { MoDiscoLogger.logError(e, JavaActivator.getDefault()); } } protected void visitClassFile(final IClassFile classFile, final String filePath) throws JavaModelException { boolean classFileHasSource = false; String fileContent = null; // test if user wants to analyse source and if this class file has an // attached source if (this.useSources) { fileContent = classFile.getSource(); if (fileContent != null) { classFileHasSource = true; } } if (classFileHasSource) { // source code retrieved, delegate model creation to a JavaReader IModelReader javaReader = new JavaReader(this.factory, null, null); javaReader.readModel(classFile, this.resultModel, this.globalBindings, new NullProgressMonitor()); } else { // no source has been retrieved ClassFileParser jdtVisitor = new ClassFileParser(this.factory, this.resultModel, this.globalBindings, this.typeFinder, filePath); jdtVisitor.parse(classFile); } } protected void resolveMethodRedefinition(final Model resultModel1) { MethodRedefinitionManager.resolveMethodRedefinitions(resultModel1, this.factory); } protected void finalResolveBindings(final Model resultModel1) { this.globalBindings.resolveBindings(resultModel1); } protected BindingManager getBindingManager() { BindingManager bindingManager = new BindingManager(this.factory); return bindingManager; } public void terminate(final IProgressMonitor monitor) { monitor.subTask(Messages.LibraryReader_BindingTask); finalResolveBindings(this.resultModel); monitor.subTask(Messages.LibraryReader_RedefinitionsTask); resolveMethodRedefinition(this.resultModel); } /** * Returns the {@link Archive} object which corresponds to the * {@link IPackageFragmentRoot#isArchive() archive} in which this class file * is contained. If a corresponding archive is present in the {@code model}, * it is returned, or a new one is created and added to the {@code model}. * * @param classFile * the class file * @param factory * the EMF factory * @param model * the {@code Model} * @return the {@code Archive object}, or {@code null} if {@code classFile} * is not contained in an archive */ public static Archive getArchive(final IClassFile classFile, final JavaFactory factory, final Model model) { Archive archive = null; IPackageFragmentRoot root = (IPackageFragmentRoot) classFile .getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT); if (root != null && root.isArchive()) { String libraryPath = getPath(root); // class file is in a library for (Archive itElement : model.getArchives()) { if (itElement.getOriginalFilePath().equals(libraryPath)) { return itElement; } } // if non present in model, create a new one archive = factory.createArchive(); archive.setName(root.getElementName()); archive.setOriginalFilePath(libraryPath); ManifestReader.completeArchiveWithManifest(root, archive, factory); model.getArchives().add(archive); } return archive; } /** * Returns the archive-relative path of the class file. If this class file * is in an archive (workspace or external), the path will be the path * inside the archive. If it is in a folder (workspace or external), the * path will be the full absolute path to this class file. * * @param classFile * the class file * @return the archive-relative path */ public static String getPath(final IClassFile classFile) { IPackageFragmentRoot library = (IPackageFragmentRoot) classFile .getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT); String filePath = null; if (library.isArchive()) { // zip or jar IPackageFragment parent = (IPackageFragment) classFile.getParent(); String packagePath = parent.getElementName().replace('.', '/'); filePath = '/' + packagePath + '/' + classFile.getElementName(); } else { // folder if (library.isExternal()) { filePath = classFile.getPath().toOSString(); } else { IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(classFile.getPath()); filePath = file.getLocation().toOSString(); } } return filePath; } /** * Returns the absolute path of this library in the filesystem. * * @param library * the library * @return the absolute path of this library */ public static String getPath(final IPackageFragmentRoot library) { String filePath = library.getPath().toOSString(); // non external resources are relative to the workspace if (!library.isExternal()) { IResource resource = null; if (library.isArchive()) { // zip or jar resource = ResourcesPlugin.getWorkspace().getRoot().getFile(library.getPath()); } else { // folder resource = ResourcesPlugin.getWorkspace().getRoot().getFolder(library.getPath()); } filePath = resource.getLocation().toOSString(); } return filePath; } /** * @see org.eclipse.jdt.core.ISourceReference#getSource() */ public static String getFileContent(final IClassFile classFile) { String source = null; try { source = classFile.getSource(); } catch (JavaModelException e) { // Nothing assert (true); // dummy code for "EmptyBlock" rule } return source; } }