package org.rubypeople.rdt.internal.core;
import java.util.ArrayList;
import java.util.Map;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.rubypeople.rdt.core.ILoadpathEntry;
import org.rubypeople.rdt.core.IParent;
import org.rubypeople.rdt.core.IRubyElement;
import org.rubypeople.rdt.core.IRubyModelStatusConstants;
import org.rubypeople.rdt.core.ISourceFolder;
import org.rubypeople.rdt.core.ISourceFolderRoot;
import org.rubypeople.rdt.core.RubyModelException;
import org.rubypeople.rdt.core.WorkingCopyOwner;
import org.rubypeople.rdt.internal.core.util.CharOperation;
import org.rubypeople.rdt.internal.core.util.MementoTokenizer;
import org.rubypeople.rdt.internal.core.util.Util;
public class SourceFolderRoot extends Openable implements ISourceFolderRoot {
/**
* The resource associated with this root.
* (an IResource or a java.io.File (for external libraries only))
*/
protected Object resource;
/**
* Constructs a package fragment root which is the root of the ruby package
* directory hierarchy.
*/
protected SourceFolderRoot(IResource resource, RubyProject project) {
super(project);
this.resource = resource;
}
/**
* @see IParent
*/
public boolean hasChildren() throws RubyModelException {
// a source folder root always has the default location (itself) as a child
return true;
}
@Override
protected boolean buildStructure(OpenableElementInfo info,
IProgressMonitor pm, Map newElements, IResource underlyingResource)
throws RubyModelException {
// check whether this source folder root can be opened
IStatus status = validateOnLoadpath();
// if (!status.isOK()) throw newRubyModelException(status);
if (!resourceExists()) throw newNotPresentException();
return computeChildren(info, newElements);
}
/**
* Compares two objects for equality;
* for <code>PackageFragmentRoot</code>s, equality is having the
* same parent, same resources, and occurrence count.
*
*/
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof SourceFolderRoot))
return false;
SourceFolderRoot other = (SourceFolderRoot) o;
return this.resource.equals(other.resource) &&
this.parent.equals(other.parent);
}
/*
* Validate whether this package fragment root is on the classpath of its project.
*/
protected IStatus validateOnLoadpath() {
IPath path = this.getPath();
try {
// check package fragment root on classpath of its project
RubyProject project = (RubyProject) getRubyProject();
ILoadpathEntry[] classpath = project.getResolvedLoadpath(true/*ignoreUnresolvedEntry*/, false/*don't generateMarkerOnError*/, false/*don't returnResolutionInProgress*/);
for (int i = 0, length = classpath.length; i < length; i++) {
ILoadpathEntry entry = classpath[i];
if (entry.getPath().equals(path)) {
return Status.OK_STATUS;
}
}
} catch(RubyModelException e){
// could not read classpath, then assume it is outside
return e.getRubyModelStatus();
}
return new RubyModelStatus(IRubyModelStatusConstants.ELEMENT_NOT_ON_CLASSPATH, this);
}
/*
* Returns the exclusion patterns from the classpath entry associated with this root.
*/
public char[][] fullExclusionPatternChars() {
try {
// if (this.isOpen() && this.getKind() != ISourceFolderRoot.K_SOURCE) return null;
LoadpathEntry entry = (LoadpathEntry)getRawLoadpathEntry();
if (entry == null) {
return null;
} else {
return entry.fullExclusionPatternChars();
}
} catch (RubyModelException e) {
return null;
}
}
public ILoadpathEntry getRawLoadpathEntry() throws RubyModelException {
ILoadpathEntry rawEntry = null;
RubyProject project = (RubyProject)this.getRubyProject();
project.getResolvedLoadpath(true/*ignoreUnresolvedEntry*/, false/*don't generateMarkerOnError*/, false/*don't returnResolutionInProgress*/); // force the reverse rawEntry cache to be populated
RubyModelManager.PerProjectInfo perProjectInfo = project.getPerProjectInfo();
if (perProjectInfo != null && perProjectInfo.resolvedPathToRawEntries != null) {
rawEntry = (ILoadpathEntry) perProjectInfo.resolvedPathToRawEntries.get(this.getPath());
}
return rawEntry;
}
/*
* Returns the inclusion patterns from the classpath entry associated with this root.
*/
public char[][] fullInclusionPatternChars() {
try {
// if (this.isOpen() && this.getKind() != ISourceFolderRoot.K_SOURCE) return null;
LoadpathEntry entry = (LoadpathEntry)getRawLoadpathEntry();
if (entry == null) {
return null;
} else {
return entry.fullInclusionPatternChars();
}
} catch (RubyModelException e) {
return null;
}
}
/**
* Compute the source folder children of this source folder root.
*
* @exception RubyModelException The resource associated with this source folder root does not exist
*/
protected boolean computeChildren(OpenableElementInfo info, Map newElements) throws RubyModelException {
try {
// the underlying resource may be a folder or a project (in the case that the project folder
// is actually the source folder root)
IResource underlyingResource = getResource();
if (underlyingResource.getType() == IResource.FOLDER || underlyingResource.getType() == IResource.PROJECT) {
ArrayList vChildren = new ArrayList(5);
IContainer rootFolder = (IContainer) underlyingResource;
computeFolderChildren(rootFolder, CharOperation.NO_STRINGS, vChildren);
IRubyElement[] children = new IRubyElement[vChildren.size()];
vChildren.toArray(children);
info.setChildren(children);
}
} catch (RubyModelException e) {
//problem resolving children; structure remains unknown
info.setChildren(new IRubyElement[]{});
throw e;
}
return true;
}
@Override
protected Object createElementInfo() {
return new SourceFolderRootInfo();
}
@Override
public int getElementType() {
return IRubyElement.SOURCE_FOLDER_ROOT;
}
public String getElementName() {
if (this.resource instanceof IFolder)
return ((IFolder) this.resource).getName();
return ""; //$NON-NLS-1$
}
public ISourceFolder createSourceFolder(String names, boolean force, IProgressMonitor monitor) throws RubyModelException {
CreateSourceFolderOperation op = new CreateSourceFolderOperation(this, names, force);
op.runOperation(monitor);
return getSourceFolder(op.pkgName);
}
/**
* Starting at this folder, create package fragments and add the fragments that are not exclused
* to the collection of children.
*
* @exception RubyModelException The resource associated with this package fragment does not exist
*/
protected void computeFolderChildren(IContainer folder, String[] pkgName, ArrayList vChildren) throws RubyModelException {
ISourceFolder pkg = getSourceFolder(pkgName);
vChildren.add(pkg); // add ourself
try {
RubyProject rubyProject = (RubyProject)getRubyProject();
RubyModelManager manager = RubyModelManager.getRubyModelManager();
IResource[] members = folder.members();
for (int i = 0, max = members.length; i < max; i++) {
IResource member = members[i];
String memberName = member.getName();
switch(member.getType()) {
case IResource.FOLDER:
if (rubyProject.contains(member)) {
String[] newNames = Util.arrayConcat(pkgName, manager.intern(memberName));
computeFolderChildren((IFolder) member, newNames, vChildren);
}
break;
case IResource.FILE:
// inclusion filter may only include files, in which case we still want to include the immediate parent package (lazily)
break;
}
}
} catch(IllegalArgumentException e){
throw new RubyModelException(e, IRubyModelStatusConstants.ELEMENT_DOES_NOT_EXIST); // could be thrown by ElementTree when path is not found
} catch (CoreException e) {
throw new RubyModelException(e);
}
}
public void delete(int updateResourceFlags, int updateModelFlags,
IProgressMonitor monitor) throws RubyModelException {
// TODO Auto-generated method stub
}
/**
* @see IRubyElement
*/
public boolean exists() {
return super.exists(); // && validateOnLoadpath().isOK();
}
public SourceFolder getSourceFolder(String[] names) {
return new SourceFolder(this, names);
}
public boolean isExternal() {
return false;
}
public IPath getPath() {
return getResource().getFullPath();
}
public IResource getResource() {
return (IResource)this.resource;
}
public IResource getUnderlyingResource() throws RubyModelException {
if (!exists()) throw newNotPresentException();
return getResource();
}
public boolean isArchive() {
return false;
}
/**
* Returns an array of non-ruby resources contained in the receiver.
*/
public Object[] getNonRubyResources() throws RubyModelException {
return ((SourceFolderRootInfo) getElementInfo()).getNonRubyResources(getRubyProject(), getResource(), this);
}
public ISourceFolder getSourceFolder(String packName) {
String[] names = Util.getTrimmedSimpleNames(packName);
return getSourceFolder(names);
}
/**
* @see RubyElement#getHandleMemento()
*/
protected char getHandleMementoDelimiter() {
return RubyElement.JEM_SOURCEFOLDERROOT;
}
/*
* @see RubyElement
*/
public IRubyElement getHandleFromMemento(String token, MementoTokenizer memento, WorkingCopyOwner owner) {
switch (token.charAt(0)) {
case JEM_SOURCE_FOLDER:
String pkgName;
if (memento.hasMoreTokens()) {
pkgName = memento.nextToken();
char firstChar = pkgName.charAt(0);
if (firstChar == JEM_RUBYSCRIPT || firstChar == JEM_COUNT) {
token = pkgName;
pkgName = ISourceFolder.DEFAULT_PACKAGE_NAME;
} else {
token = null;
}
} else {
pkgName = ISourceFolder.DEFAULT_PACKAGE_NAME;
token = null;
}
RubyElement pkg = (RubyElement)getSourceFolder(pkgName);
if (token == null) {
return pkg.getHandleFromMemento(memento, owner);
} else {
return pkg.getHandleFromMemento(token, memento, owner);
}
}
return null;
}
/**
* @see RubyElement#getHandleMemento(StringBuffer)
*/
protected void getHandleMemento(StringBuffer buff) {
IPath path;
IResource underlyingResource = getResource();
if (underlyingResource != null) {
// internal jar or regular root
if (getResource().getProject().equals(getRubyProject().getProject())) {
path = underlyingResource.getProjectRelativePath();
} else {
path = underlyingResource.getFullPath();
}
} else {
// external jar
path = getPath();
}
((RubyElement)getParent()).getHandleMemento(buff);
buff.append(getHandleMementoDelimiter());
escapeMementoName(buff, path.toString());
}
}