blob: dc6cd4e4a5467681605cd08b68b9ad656165d697 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2007 IBM Corporation and others.
* 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
*
*******************************************************************************/
package org.eclipse.dltk.internal.core.util;
import java.util.HashMap;
import java.util.HashSet;
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.Path;
import org.eclipse.dltk.compiler.CharOperation;
import org.eclipse.dltk.compiler.env.lookup.Scope;
import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.core.DLTKLanguageManager;
import org.eclipse.dltk.core.IBuildpathEntry;
import org.eclipse.dltk.core.IDLTKLanguageToolkit;
import org.eclipse.dltk.core.IModelElement;
import org.eclipse.dltk.core.IProjectFragment;
import org.eclipse.dltk.core.IScriptFolder;
import org.eclipse.dltk.core.IScriptProject;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.dltk.core.search.IDLTKSearchScope;
import org.eclipse.dltk.internal.compiler.lookup.TypeScope;
import org.eclipse.dltk.internal.core.Model;
import org.eclipse.dltk.internal.core.ModelManager;
import org.eclipse.dltk.internal.core.Openable;
import org.eclipse.dltk.internal.core.ScriptProject;
/**
* Creates script element handles.
*/
public class HandleFactory {
/**
* Cache package fragment root information to optimize speed performance.
*/
private String lastPkgFragmentRootPath;
private IProjectFragment lastPkgFragmentRoot;
/**
* Cache package handles to optimize memory.
*/
private HashtableOfArrayToObject packageHandles;
private Model model;
public HandleFactory() {
this.model = ModelManager.getModelManager().getModel();
}
/**
* Creates an Openable handle from the given resource path.
*
* If not null, uses the given scope as a hint for getting DLTK project
* handles.
*/
public Openable createOpenable(String resourcePath, IDLTKSearchScope scope) {
int separatorIndex;
if ((separatorIndex = resourcePath
.indexOf(IDLTKSearchScope.FILE_ENTRY_SEPARATOR)) > -1) {
// path to a class file inside a archive
// Optimization: cache package fragment root handle and package
// handles
int rootPathLength;
if (this.lastPkgFragmentRootPath == null
|| (rootPathLength = this.lastPkgFragmentRootPath.length()) != resourcePath
.length()
|| !resourcePath.regionMatches(0,
this.lastPkgFragmentRootPath, 0, rootPathLength)) {
String archivePath = resourcePath.substring(0, separatorIndex);
IProjectFragment root = this.getArchiveProjectFragment(
archivePath, scope);
if (root == null)
return null; // match is outside classpath
this.lastPkgFragmentRootPath = archivePath;
this.lastPkgFragmentRoot = root;
this.packageHandles = new HashtableOfArrayToObject(5);
}
// create handle
String classFilePath = resourcePath.substring(separatorIndex + 1);
String[] simpleNames = Path.fromPortableString(classFilePath)
.segments();
String[] pkgName;
int length = simpleNames.length - 1;
if (length > 0) {
pkgName = new String[length];
System.arraycopy(simpleNames, 0, pkgName, 0, length);
} else {
pkgName = CharOperation.NO_STRINGS;
}
IScriptFolder pkgFragment = (IScriptFolder) this.packageHandles
.get(pkgName);
if (pkgFragment == null) {
pkgFragment = this.lastPkgFragmentRoot
.getScriptFolder(toPath(pkgName));
this.packageHandles.put(pkgName, pkgFragment);
}
ISourceModule classFile = pkgFragment
.getSourceModule(simpleNames[length]);
return (Openable) classFile;
} else {
// path to a file in a directory
// Optimization: cache package fragment root handle and package
// handles
int rootPathLength = -1;
if (this.lastPkgFragmentRootPath == null
|| !(resourcePath.startsWith(this.lastPkgFragmentRootPath)
&& (rootPathLength = this.lastPkgFragmentRootPath
.length()) > 0 && resourcePath
.charAt(rootPathLength) == '/')) {
IProjectFragment root = this.getProjectFragment(resourcePath);
if (root == null)
return null; // match is outside classpath
this.lastPkgFragmentRoot = root;
this.lastPkgFragmentRootPath = this.lastPkgFragmentRoot
.getPath().toString();
this.packageHandles = new HashtableOfArrayToObject(5);
}
// create handle
resourcePath = resourcePath.substring(this.lastPkgFragmentRootPath
.length());
String[] simpleNames = Path.fromPortableString(resourcePath)
.segments();
String[] pkgName;
int length = simpleNames.length - 1;
if (length > 0) {
pkgName = new String[length];
System.arraycopy(simpleNames, 0, pkgName, 0, length);
} else {
pkgName = CharOperation.NO_STRINGS;
}
IScriptFolder pkgFragment = (IScriptFolder) this.packageHandles
.get(pkgName);
if (pkgFragment == null) {
IPath ppath = toPath(pkgName);
IProjectFragment root = this.lastPkgFragmentRoot;
pkgFragment = root.getScriptFolder(ppath);
this.packageHandles.put(pkgName, pkgFragment);
}
String simpleName = simpleNames[length];
ISourceModule unit = pkgFragment.getSourceModule(simpleName);
return (Openable) unit;
}
}
private IPath toPath(String[] pkgName) {
IPath path = new Path(""); //$NON-NLS-1$
for (int i = 0; i < pkgName.length; ++i) {
path = path.append(pkgName[i]);
}
return path;
}
/**
* Returns a handle denoting the class member identified by its scope.
*/
public IModelElement createElement(TypeScope scope, ISourceModule unit,
HashSet existingElements, HashMap knownScopes) {
return createElement(scope, scope.referenceContext.sourceStart(), unit,
existingElements, knownScopes);
}
/**
* Create handle by adding child to parent obtained by recursing into parent
* scopes.
*/
private IModelElement createElement(Scope scope, int elementPosition,
ISourceModule unit, HashSet existingElements, HashMap knownScopes) {
if (DLTKCore.DEBUG) {
System.err.println("TODO: HandleFactory: Add implementation..."); //$NON-NLS-1$
}
return null;
}
/**
* Returns the package fragment root that corresponds to the given jar path.
* See createOpenable(...) for the format of the jar path string. If not
* null, uses the given scope as a hint for getting script project handles.
*/
private IProjectFragment getArchiveProjectFragment(
String archivePathString, IDLTKSearchScope scope) {
IPath archivePath = Path.fromPortableString(archivePathString);
Object target = Model.getTarget(ResourcesPlugin.getWorkspace()
.getRoot(), archivePath, false);
if (target instanceof IFile) {
// internal jar: is it on the classpath of its project?
// e.g. org.eclipse.swt.win32/ws/win32/swt.jar
// is NOT on the classpath of org.eclipse.swt.win32
IFile archiveFile = (IFile) target;
ScriptProject scriptProject = (ScriptProject) this.model
.getScriptProject(archiveFile);
IBuildpathEntry[] classpathEntries;
try {
classpathEntries = scriptProject.getResolvedBuildpath();
for (int j = 0, entryCount = classpathEntries.length; j < entryCount; j++) {
if (classpathEntries[j].getPath().equals(archivePath)) {
return scriptProject.getProjectFragment(archiveFile);
}
}
} catch (ModelException e) {
// ignore and try to find another project
}
}
// walk projects in the scope and find the first one that has the given
// archive path in its classpath
IScriptProject[] projects;
if (scope != null) {
IPath[] enclosingProjectsAndArchives = scope
.enclosingProjectsAndZips();
int length = enclosingProjectsAndArchives.length;
projects = new IScriptProject[length];
int index = 0;
for (int i = 0; i < length; i++) {
IPath path = enclosingProjectsAndArchives[i];
if (!org.eclipse.dltk.compiler.util.Util.isArchiveFileName(
scope.getLanguageToolkit(), path.lastSegment())) {
projects[index++] = this.model.getScriptProject(path
.segment(0));
}
}
if (index < length) {
System.arraycopy(projects, 0,
projects = new IScriptProject[index], 0, index);
}
IProjectFragment root = getArchiveFolder(archivePath, target,
projects);
if (root != null) {
return root;
}
}
// not found in the scope, walk all projects
try {
IDLTKLanguageToolkit toolkit = null;
if (scope != null) {
toolkit = scope.getLanguageToolkit();
}
if (toolkit == null) {
return null;
}
projects = this.model.getScriptProjects(toolkit.getNatureId());
} catch (ModelException e) {
// script model is not accessible
return null;
}
return getArchiveFolder(archivePath, target, projects);
}
private IProjectFragment getArchiveFolder(IPath archivePath, Object target,
IScriptProject[] projects) {
for (int i = 0, projectCount = projects.length; i < projectCount; i++) {
try {
ScriptProject scriptProject = (ScriptProject) projects[i];
IBuildpathEntry classpathEntry = scriptProject
.getBuildpathEntryFor(archivePath);
if (classpathEntry != null) {
if (target instanceof IFile) {
// internal jar
return scriptProject.getProjectFragment((IFile) target);
} else {
// external jar
return scriptProject.getProjectFragment0(archivePath);
}
}
} catch (ModelException e) {
// ModelException from getResolvedClasspath - a problem occured
// while accessing project: nothing we can do, ignore
}
}
return null;
}
private static IProject[] getAllProjects() {
return ResourcesPlugin.getWorkspace().getRoot().getProjects();
}
/**
* Returns the package fragment root that contains the given resource path.
*/
private IProjectFragment getProjectFragment(String pathString) {
IPath path = Path.fromPortableString(pathString);
IProject[] projects = getAllProjects();
for (int i = 0, max = projects.length; i < max; i++) {
try {
IProject project = projects[i];
if (!project.isAccessible()
|| !DLTKLanguageManager.hasScriptNature(project))
continue;
IScriptProject scriptProject = this.model
.getScriptProject(project);
IProjectFragment[] roots = scriptProject.getProjectFragments();
for (int j = 0, rootCount = roots.length; j < rootCount; j++) {
IProjectFragment root = roots[j];
if (root.getPath().isPrefixOf(path)
&& !Util.isExcluded(path, root, false)) {
return root;
}
}
} catch (CoreException e) {
// CoreException from hasNature - should not happen since we
// check that the project is accessible
// ModelException from getProjectFragments - a problem occured
// while accessing project: nothing we can do, ignore
}
}
return null;
}
}