/*******************************************************************************
* Copyright (c) 2000, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.rubypeople.rdt.internal.core;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.rubypeople.rdt.core.ILoadpathEntry;
import org.rubypeople.rdt.core.IRubyElement;
import org.rubypeople.rdt.core.ISourceFolderRoot;
import org.rubypeople.rdt.core.RubyModelException;
import org.rubypeople.rdt.internal.core.util.HashtableOfArrayToObject;
/**
* Info for IRubyProject.
* <p>
* Note: <code>getChildren()</code> returns all of the
* <code>IPackageFragmentRoots</code> specified on the classpath for the
* project. This can include roots external to the project. See
* <code>RubyProject#getAllPackageFragmentRoots()</code> and
* <code>RubyProject#getPackageFragmentRoots()</code>. To get only the
* <code>IPackageFragmentRoots</code> that are internal to the project, use
* <code>RubyProject#getChildren()</code>.
*/
/* package */
class RubyProjectElementInfo extends OpenableElementInfo {
/**
* A array with all the non-ruby resources contained by this PackageFragment
*/
private Object[] nonRubyResources;
public ProjectCache projectCache;
static class ProjectCache {
ProjectCache(ISourceFolderRoot[] allPkgFragmentRootsCache, HashtableOfArrayToObject allPkgFragmentsCache, HashtableOfArrayToObject isPackageCache, Map rootToResolvedEntries) {
this.allPkgFragmentRootsCache = allPkgFragmentRootsCache;
this.allPkgFragmentsCache = allPkgFragmentsCache;
this.isPackageCache = isPackageCache;
this.rootToResolvedEntries = rootToResolvedEntries;
}
/*
* A cache of all package fragment roots of this project.
*/
public ISourceFolderRoot[] allPkgFragmentRootsCache;
/*
* A cache of all package fragments in this project.
* (a map from String[] (the package name) to IPackageFragmentRoot[] (the package fragment roots that contain a package fragment with this name)
*/
public HashtableOfArrayToObject allPkgFragmentsCache;
/*
* A set of package names (String[]) that are known to be packages.
*/
public HashtableOfArrayToObject isPackageCache;
public Map rootToResolvedEntries;
}
/**
* Create and initialize a new instance of the receiver
*/
public RubyProjectElementInfo() {
this.nonRubyResources = null;
}
/**
* Compute the non-ruby resources contained in this ruby project.
*/
private Object[] computeNonRubyResources(RubyProject project) {
// determine if src == project and/or if bin == project
IPath projectPath = project.getProject().getFullPath();
boolean srcIsProject = false;
char[][] inclusionPatterns = null;
char[][] exclusionPatterns = null;
ILoadpathEntry[] classpath = null;
try {
classpath = project.getResolvedLoadpath(true/* ignoreUnresolvedEntry */, false/* don't generateMarkerOnError */, false/* don't returnResolutionInProgress */);
for (int i = 0; i < classpath.length; i++) {
ILoadpathEntry entry = classpath[i];
if (projectPath.equals(entry.getPath())) {
srcIsProject = true;
inclusionPatterns = ((LoadpathEntry) entry).fullInclusionPatternChars();
exclusionPatterns = ((LoadpathEntry) entry).fullExclusionPatternChars();
break;
}
}
} catch (RubyModelException e) {
// ignore
}
Object[] resources = new IResource[5];
int resourcesCounter = 0;
try {
IResource[] members = ((IContainer) project.getResource()).members();
for (int i = 0, max = members.length; i < max; i++) {
IResource res = members[i];
switch (res.getType()) {
case IResource.FILE:
IPath resFullPath = res.getFullPath();
String resName = res.getName();
// ignore .rb file if src == project
if (srcIsProject && !org.rubypeople.rdt.internal.core.util.Util.isExcluded(res, inclusionPatterns, exclusionPatterns)) {
break;
}
// else add non ruby resource
if (resources.length == resourcesCounter) {
// resize
System.arraycopy(resources, 0, (resources = new IResource[resourcesCounter * 2]), 0, resourcesCounter);
}
resources[resourcesCounter++] = res;
break;
case IResource.FOLDER:
resFullPath = res.getFullPath();
// ignore non-excluded folders on the loadpath or that
// correspond to an output location
if ((srcIsProject && !org.rubypeople.rdt.internal.core.util.Util.isExcluded(res, inclusionPatterns, exclusionPatterns)) || this.isLoadpathEntryOrOutputLocation(resFullPath, classpath)) {
break;
}
// else add non ruby resource
if (resources.length == resourcesCounter) {
// resize
System.arraycopy(resources, 0, (resources = new IResource[resourcesCounter * 2]), 0, resourcesCounter);
}
resources[resourcesCounter++] = res;
}
}
if (resources.length != resourcesCounter) {
System.arraycopy(resources, 0, (resources = new IResource[resourcesCounter]), 0, resourcesCounter);
}
} catch (CoreException e) {
resources = NO_NON_RUBY_RESOURCES;
resourcesCounter = 0;
}
return resources;
}
/**
* Returns an array of non-ruby resources contained in the receiver.
*/
Object[] getNonRubyResources(RubyProject project) {
if (this.nonRubyResources == null) {
this.nonRubyResources = computeNonRubyResources(project);
}
return this.nonRubyResources;
}
/*
* Returns whether the given path is a loadpath entry
*/
private boolean isLoadpathEntryOrOutputLocation(IPath path, ILoadpathEntry[] resolvedLoadpath) {
for (int i = 0, length = resolvedLoadpath.length; i < length; i++) {
ILoadpathEntry entry = resolvedLoadpath[i];
if (entry.getPath().equals(path)) { return true; }
}
return false;
}
/*
* Reset the package fragment roots and package fragment caches
*/
void resetCaches() {
this.projectCache = null;
// JavaModelManager.getJavaModelManager().resetJarTypeCache();
}
/**
* Set the fNonRubyResources to res value
*/
void setNonRubyResources(Object[] resources) {
this.nonRubyResources = resources;
}
ProjectCache getProjectCache(RubyProject project) {
ProjectCache cache = this.projectCache;
if (cache == null) {
ISourceFolderRoot[] roots;
Map reverseMap = new HashMap(3);
try {
roots = project.getAllSourceFolderRoots(reverseMap);
} catch (RubyModelException e) {
// project does not exist: cannot happen since this is the info of the project
roots = new ISourceFolderRoot[0];
reverseMap.clear();
}
HashtableOfArrayToObject fragmentsCache = new HashtableOfArrayToObject();
HashtableOfArrayToObject isPackageCache = new HashtableOfArrayToObject();
for (int i = 0, length = roots.length; i < length; i++) {
ISourceFolderRoot root = roots[i];
IRubyElement[] frags = null;
try {
// if (root.isArchive() && !root.isOpen()) {
// JarPackageFragmentRootInfo info = new JarPackageFragmentRootInfo();
// ((JarPackageFragmentRoot) root).computeChildren(info, new HashMap());
// frags = info.children;
// } else
frags = root.getChildren();
} catch (RubyModelException e) {
// root doesn't exist: ignore
continue;
}
for (int j = 0, length2 = frags.length; j < length2; j++) {
SourceFolder fragment= (SourceFolder) frags[j];
String[] pkgName = fragment.names;
Object existing = fragmentsCache.get(pkgName);
if (existing == null) {
fragmentsCache.put(pkgName, root);
// cache whether each package and its including packages (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=119161)
// are actual packages
addNames(pkgName, isPackageCache);
} else {
if (existing instanceof SourceFolderRoot) {
fragmentsCache.put(pkgName, new ISourceFolderRoot[] {(SourceFolderRoot) existing, root});
} else {
ISourceFolderRoot[] entry= (ISourceFolderRoot[]) existing;
ISourceFolderRoot[] copy= new ISourceFolderRoot[entry.length + 1];
System.arraycopy(entry, 0, copy, 0, entry.length);
copy[entry.length]= root;
fragmentsCache.put(pkgName, copy);
}
}
}
}
cache = new ProjectCache(roots, fragmentsCache, isPackageCache, reverseMap);
this.projectCache = cache;
}
return cache;
}
/*
* Adds the given name and its super names to the given set
* (e.g. for {"a", "b", "c"}, adds {"a", "b", "c"}, {"a", "b"}, and {"a"})
*/
public static void addNames(String[] name, HashtableOfArrayToObject set) {
set.put(name, name);
int length = name.length;
for (int i = length-1; i > 0; i--) {
String[] superName = new String[i];
System.arraycopy(name, 0, superName, 0, i);
set.put(superName, superName);
}
}
}