|
From: <caw...@us...> - 2006-12-15 15:31:23
|
Revision: 1705
http://svn.sourceforge.net/rubyeclipse/?rev=1705&view=rev
Author: cawilliams
Date: 2006-12-15 07:31:22 -0800 (Fri, 15 Dec 2006)
Log Message:
-----------
all sorts of plumbing for new test case wizard, new SourceFolder in IRubyElement hierarchy
Modified Paths:
--------------
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/IRubyElement.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/IRubyProject.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/IType.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/RubyConventions.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/Openable.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyElement.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyModel.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyModelManager.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyModelOperation.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyProject.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyScriptStructureBuilder.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyType.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/SourceRefElement.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/buffer/LRUCache.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/symbols/ISymbolFinder.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/util/CharOperation.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/util/Util.java
Added Paths:
-----------
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/ISourceFolder.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/CreateMethodOperation.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/CreateRubyScriptOperation.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/CreateSourceFolderOperation.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/SourceFolder.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/SourceFolderInfo.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/util/ASTRewrite.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/util/ASTUtil.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/util/DOMFinder.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/util/WeakHashSet.java
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/IRubyElement.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/IRubyElement.java 2006-12-15 15:29:19 UTC (rev 1704)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/IRubyElement.java 2006-12-15 15:31:22 UTC (rev 1705)
@@ -32,20 +32,29 @@
public interface IRubyElement extends IAdaptable {
public static final int RUBY_MODEL = 0;
+ /**
+ * Constant representing a Ruby project.
+ * A Ruby element with this type can be safely cast to <code>IRubyProject</code>.
+ */
public static final int RUBY_PROJECT = 1;
- public static final int SCRIPT = 2;
- public static final int TYPE = 3;
- public static final int METHOD = 4;
- public static final int GLOBAL = 5;
- public static final int IMPORT_DECLARATION = 6;
- public static final int CONSTANT = 7;
- public static final int CLASS_VAR = 8;
- public static final int INSTANCE_VAR = 9;
- public static final int LOCAL_VARIABLE = 10;
- public static final int BLOCK = 11;
- public static final int DYNAMIC_VAR = 12;
- public static final int FIELD = 13;
- public static final int IMPORT_CONTAINER = 14;
+ /**
+ * Constant representing a source folder
+ * A Ruby element with this type can be safely cast to <code>ISourceFolder</code>.
+ */
+ public static final int SOURCE_FOLDER = 2;
+ public static final int SCRIPT = 3;
+ public static final int TYPE = 4;
+ public static final int METHOD = 5;
+ public static final int GLOBAL = 6;
+ public static final int IMPORT_DECLARATION = 7;
+ public static final int CONSTANT = 8;
+ public static final int CLASS_VAR = 9;
+ public static final int INSTANCE_VAR = 10;
+ public static final int LOCAL_VARIABLE = 11;
+ public static final int BLOCK = 12;
+ public static final int DYNAMIC_VAR = 13;
+ public static final int FIELD = 14;
+ public static final int IMPORT_CONTAINER = 15;
/**
* Returns the first ancestor of this Ruby element that has the given type.
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/IRubyProject.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/IRubyProject.java 2006-12-15 15:29:19 UTC (rev 1704)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/IRubyProject.java 2006-12-15 15:31:22 UTC (rev 1705)
@@ -29,7 +29,9 @@
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
/**
* @author Chris
@@ -112,4 +114,13 @@
public IRubyScript getRubyScript(IFile file);
+ public abstract ISourceFolder getSourceFolder(String[] names);
+
+ public abstract ISourceFolder getSourceFolder(IResource resource);
+
+ public abstract ISourceFolder[] getSourceFolders() throws RubyModelException;
+
+ public abstract ISourceFolder createSourceFolder(String packName,
+ boolean force, IProgressMonitor monitor) throws RubyModelException;
+
}
\ No newline at end of file
Added: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/ISourceFolder.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/ISourceFolder.java (rev 0)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/ISourceFolder.java 2006-12-15 15:31:22 UTC (rev 1705)
@@ -0,0 +1,118 @@
+package org.rubypeople.rdt.core;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+
+public interface ISourceFolder extends IRubyElement, IParent, IOpenable {
+
+ /**
+ * Returns whether this fragment contains at least one Ruby resource.
+ * @return true if this fragment contains at least one Ruby resource, false otherwise
+ * @exception RubyModelException if this element does not exist or if an
+ * exception occurs while accessing its corresponding resource.
+ */
+ boolean containsRubyResources() throws RubyModelException;
+ /**
+ * Creates and returns a compilation unit in this package fragment
+ * with the specified name and contents. No verification is performed
+ * on the contents.
+ *
+ * <p>It is possible that a compilation unit with the same name already exists in this
+ * package fragment.
+ * The value of the <code>force</code> parameter effects the resolution of
+ * such a conflict:<ul>
+ * <li> <code>true</code> - in this case the compilation is created with the new contents</li>
+ * <li> <code>false</code> - in this case a <code>RubyModelException</code> is thrown</li>
+ * </ul>
+ *
+ * @param contents the given contents
+ * @param force specify how to handle conflict is the same name already exists
+ * @param monitor the given progress monitor
+ * @param name the given name
+ * @exception RubyModelException if the element could not be created. Reasons include:
+ * <ul>
+ * <li> This Ruby element does not exist (ELEMENT_DOES_NOT_EXIST)</li>
+ * <li> A <code>CoreException</code> occurred while creating an underlying resource
+ * <li> The name is not a valid compilation unit name (INVALID_NAME)
+ * <li> The contents are <code>null</code> (INVALID_CONTENTS)
+ * </ul>
+ * @return a compilation unit in this package fragment
+ * with the specified name and contents
+ */
+ IRubyScript createRubyScript(String name, String contents, boolean force, IProgressMonitor monitor) throws RubyModelException;
+ /**
+ * Returns the compilation unit with the specified name
+ * in this package (for example, <code>"Object.java"</code>).
+ * The name has to be a valid compilation unit name.
+ * This is a handle-only method. The compilation unit may or may not be present.
+ *
+ * @param name the given name
+ * @return the compilation unit with the specified name in this package
+ * @see RubyConventions#validateCompilationUnitName(String)
+ */
+// IRubyScript getRubyScript(String name);
+ /**
+ * Returns all of the compilation units in this package fragment.
+ *
+ * <p>Note: it is possible that a package fragment contains only
+ * class files (in other words, its kind is <code>K_BINARY</code>), in which
+ * case this method returns an empty collection.
+ * </p>
+ *
+ * @exception RubyModelException if this element does not exist or if an
+ * exception occurs while accessing its corresponding resource.
+ * @return all of the compilation units in this package fragment
+ */
+ IRubyScript[] getRubyScripts() throws RubyModelException;
+ /**
+ * Returns all of the compilation units in this package fragment that are
+ * in working copy mode and that have the given owner.
+ * <p>
+ * Only existing working copies are returned. So a compilation unit handle that has no
+ * corresponding resource on disk will be included if and only if is in working copy mode.
+ * </p>
+ * <p>Note: it is possible that a package fragment contains only
+ * class files (in other words, its kind is <code>K_BINARY</code>), in which
+ * case this method returns an empty collection.
+ * </p>
+ *
+ * @param owner the owner of the returned compilation units
+ * @exception RubyModelException if this element does not exist or if an
+ * exception occurs while accessing its corresponding resource.
+ * @return all of the compilation units in this package fragment
+ * @since 3.0
+ */
+ IRubyScript[] getRubyScripts(WorkingCopyOwner owner) throws RubyModelException;
+ /**
+ * Returns the dot-separated package name of this fragment, for example
+ * <code>"java.lang"</code>, or <code>""</code> (the empty string),
+ * for the default package.
+ *
+ * @return the dot-separated package name of this fragment
+ */
+ String getElementName();
+
+ /**
+ * Returns an array of non-Ruby resources contained in this package fragment.
+ * <p>
+ * Non-Ruby resources includes other files and folders located in the same
+ * directory as the compilation units or class files for this package
+ * fragment. Source files excluded from this package by virtue of
+ * inclusion/exclusion patterns on the corresponding source classpath entry
+ * are considered non-Ruby resources and will appear in the result
+ * (possibly in a folder).
+ * </p>
+ *
+ * @exception RubyModelException if this element does not exist or if an
+ * exception occurs while accessing its corresponding resource.
+ * @return an array of non-Ruby resources (<code>IFile</code>s,
+ * <code>IFolder</code>s, or <code>IStorage</code>s if the
+ * package fragment is in an archive) contained in this package
+ * fragment
+ * @see IClasspathEntry#getInclusionPatterns()
+ * @see IClasspathEntry#getExclusionPatterns()
+ */
+ Object[] getNonRubyResources() throws RubyModelException;
+ IRubyScript getRubyScript(String name);
+
+}
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/IType.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/IType.java 2006-12-15 15:29:19 UTC (rev 1704)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/IType.java 2006-12-15 15:31:22 UTC (rev 1705)
@@ -24,6 +24,7 @@
*/
package org.rubypeople.rdt.core;
+import org.eclipse.core.runtime.IProgressMonitor;
import org.rubypeople.rdt.internal.core.RubyMethod;
/**
@@ -137,4 +138,38 @@
public boolean isMember() throws RubyModelException;
+ /**
+ * Creates and returns a method or constructor in this type with the
+ * given contents.
+ * <p>
+ * Optionally, the new element can be positioned before the specified
+ * sibling. If no sibling is specified, the element will be appended
+ * to this type.
+ *
+ * <p>It is possible that a method with the same signature already exists in this type.
+ * The value of the <code>force</code> parameter effects the resolution of
+ * such a conflict:<ul>
+ * <li> <code>true</code> - in this case the method is created with the new contents</li>
+ * <li> <code>false</code> - in this case a <code>RubyModelException</code> is thrown</li>
+ * </ul></p>
+ *
+ * @param contents the given contents
+ * @param sibling the given sibling
+ * @param force a flag in case the same name already exists in this type
+ * @param monitor the given progress monitor
+ * @exception RubyModelException if the element could not be created. Reasons include:
+ * <ul>
+ * <li> This Ruby element does not exist (ELEMENT_DOES_NOT_EXIST)</li>
+ * <li> A <code>CoreException</code> occurred while updating an underlying resource
+ * <li> The specified sibling is not a child of this type (INVALID_SIBLING)
+ * <li> The contents could not be recognized as a method or constructor
+ * declaration (INVALID_CONTENTS)
+ * <li> This type is read-only (binary) (READ_ONLY)
+ * <li> There was a naming collision with an existing method (NAME_COLLISION)
+ * </ul>
+ * @return a method or constructor in this type with the given contents
+ */
+ public IMethod createMethod(String contents, IRubyElement sibling, boolean force,
+ IProgressMonitor progress) throws RubyModelException;
+
}
\ No newline at end of file
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/RubyConventions.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/RubyConventions.java 2006-12-15 15:29:19 UTC (rev 1704)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/RubyConventions.java 2006-12-15 15:31:22 UTC (rev 1705)
@@ -8,6 +8,7 @@
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.rubypeople.rdt.internal.core.RubyModelStatus;
+import org.rubypeople.rdt.internal.core.util.Messages;
import org.rubypeople.rdt.internal.core.util.Util;
/**
@@ -72,4 +73,33 @@
// Util.bind("convention.illegalIdentifier", id), null); //$NON-NLS-1$
// }
}
+
+ public static IStatus validateRubyTypeName(String typeName) {
+ if (typeName == null) {
+ return new Status(IStatus.ERROR, RubyCore.PLUGIN_ID, -1, Messages.convention_type_nullName, null);
+ }
+ if (typeName.length() == 0) {
+ return new Status(IStatus.ERROR, RubyCore.PLUGIN_ID, -1, Messages.bind(Messages.convention_type_invalidName, typeName), null);
+ }
+ if (!isConstant(typeName)) {
+ return new Status(IStatus.ERROR, RubyCore.PLUGIN_ID, -1, "Class name must be a constant. It must begin with a capital letter, and contain only letters, digits, or underscores.", null);
+ }
+ return new Status(IStatus.OK, RubyCore.PLUGIN_ID, -1, null, null);
+
+ }
+
+ private static boolean isConstant(String className) {
+ if (className == null || className.length() == 0) return false;
+ if (!Character.isLowerCase(className.charAt(0)) && !Character.isLetter(className.charAt(0)))
+ return false;
+ int namespaceDelimeterIndex = className.indexOf("::");
+ if (namespaceDelimeterIndex != -1) {
+ return isConstant(className.substring(0, namespaceDelimeterIndex)) && isConstant(className.substring(namespaceDelimeterIndex+2));
+ }
+ for (int i = 0; i < className.length(); i++) {
+ char c = className.charAt(i);
+ if (!Character.isLetterOrDigit(c) && c != '_') return false;
+ }
+ return true;
+ }
}
Added: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/CreateMethodOperation.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/CreateMethodOperation.java (rev 0)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/CreateMethodOperation.java 2006-12-15 15:31:22 UTC (rev 1705)
@@ -0,0 +1,272 @@
+package org.rubypeople.rdt.internal.core;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.text.edits.TextEdit;
+import org.jruby.ast.DefnNode;
+import org.jruby.ast.DefsNode;
+import org.jruby.ast.NewlineNode;
+import org.jruby.ast.Node;
+import org.rubypeople.rdt.core.IRubyElement;
+import org.rubypeople.rdt.core.IRubyModelStatus;
+import org.rubypeople.rdt.core.IRubyModelStatusConstants;
+import org.rubypeople.rdt.core.IRubyScript;
+import org.rubypeople.rdt.core.IType;
+import org.rubypeople.rdt.core.RubyModelException;
+import org.rubypeople.rdt.internal.core.parser.RubyParser;
+import org.rubypeople.rdt.internal.core.util.ASTRewrite;
+import org.rubypeople.rdt.internal.core.util.ASTUtil;
+import org.rubypeople.rdt.internal.core.util.Messages;
+
+public class CreateMethodOperation extends RubyModelOperation {
+
+ /**
+ * A constant meaning to position the new element
+ * as the last child of its parent element.
+ */
+ protected static final int INSERT_LAST = 1;
+ /**
+ * A constant meaning to position the new element
+ * after the element defined by <code>fAnchorElement</code>.
+ */
+ protected static final int INSERT_AFTER = 2;
+ /**
+ * A constant meaning to position the new element
+ * before the element defined by <code>fAnchorElement</code>.
+ */
+ protected static final int INSERT_BEFORE = 3;
+ /**
+ * One of the position constants, describing where
+ * to position the newly created element.
+ */
+ protected int insertionPolicy = INSERT_LAST;
+ /**
+ * The element that the newly created element is
+ * positioned relative to, as described by
+ * <code>fInsertPosition</code>, or <code>null</code>
+ * if the newly created element will be positioned
+ * last.
+ */
+ protected IRubyElement anchorElement = null;
+ /**
+ * A flag indicating whether creation of a new element occurred.
+ * A request for creating a duplicate element would request in this
+ * flag being set to <code>false</code>. Ensures that no deltas are generated
+ * when creation does not occur.
+ */
+ protected boolean creationOccurred = true;
+
+ private String source;
+ private Node cuAST;
+ private Node createdNode;
+ private String[] parameters;
+
+ public CreateMethodOperation(IType parentElement, String source, boolean force) {
+ super(null, new IRubyElement[]{parentElement}, force);
+ this.source = source;
+ }
+
+ @Override
+ protected void executeOperation() throws RubyModelException {
+ try {
+ beginTask(getMainTaskName(), getMainAmountOfWork());
+ RubyElementDelta delta = newRubyElementDelta();
+ IRubyScript unit = getRubyScript();
+ generateNewRubyScriptAST(unit);
+ if (this.creationOccurred) {
+ //a change has really occurred
+ unit.save(null, false);
+ boolean isWorkingCopy = unit.isWorkingCopy();
+ if (!isWorkingCopy)
+ this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
+ worked(1);
+ resultElements = generateResultHandles();
+ if (!isWorkingCopy // if unit is working copy, then save will have already fired the delta
+ && unit.getParent().exists()) {
+ for (int i = 0; i < resultElements.length; i++) {
+ delta.added(resultElements[i]);
+ }
+ addDelta(delta);
+ } // else unit is created outside classpath
+ // non-ruby resource delta will be notified by delta processor
+ }
+ } finally {
+ done();
+ }
+
+ }
+
+ /**
+ * Returns the IType the member is to be created in.
+ */
+ protected IType getType() {
+ return (IType)getParentElement();
+ }
+
+ /**
+ * @see CreateElementInCUOperation#generateResultHandle
+ */
+ protected IRubyElement generateResultHandle() {
+ String[] types = convertASTMethodTypesToSignatures();
+ String name = getASTNodeName();
+ return getType().getMethod(name, types);
+ }
+
+ private String getASTNodeName() {
+ if (this.createdNode instanceof DefsNode)
+ return ((DefsNode) this.createdNode).getName();
+ return ((DefnNode) this.createdNode).getName();
+ }
+
+ /**
+ * Returns the type signatures of the parameter types of the
+ * current <code>MethodDeclaration</code>
+ */
+ protected String[] convertASTMethodTypesToSignatures() {
+ if (this.parameters == null) {
+ if (this.createdNode != null) {
+ DefnNode methodDeclaration = (DefnNode) this.createdNode;
+ this.parameters = ASTUtil.getArgs(methodDeclaration.getArgsNode(), methodDeclaration.getBodyNode());
+ }
+ }
+ return this.parameters;
+ }
+
+
+
+ /**
+ * Creates and returns the handles for the elements this operation created.
+ */
+ protected IRubyElement[] generateResultHandles() {
+ return new IRubyElement[]{generateResultHandle()};
+ }
+
+ /**
+ * Returns the ruby script in which the new element is being created.
+ */
+ protected IRubyScript getRubyScript() {
+ return getRubyScriptFor(getParentElement());
+ }
+
+ /**
+ * @see CreateElementInCUOperation#getMainTaskName()
+ */
+ public String getMainTaskName(){
+ return Messages.operation_createMethodProgress;
+ }
+
+ /**
+ * Returns the amount of work for the main task of this operation for
+ * progress reporting.
+ */
+ protected int getMainAmountOfWork(){
+ return 2;
+ }
+
+ /**
+ * Instructs this operation to position the new element before
+ * the given sibling, or to add the new element as the last child
+ * of its parent if <code>null</code>.
+ */
+ public void createBefore(IRubyElement sibling) {
+ setRelativePosition(sibling, INSERT_BEFORE);
+ }
+
+ /**
+ * Instructs this operation to position the new element relative
+ * to the given sibling, or to add the new element as the last child
+ * of its parent if <code>null</code>. The <code>position</code>
+ * must be one of the position constants.
+ */
+ protected void setRelativePosition(IRubyElement sibling, int policy) throws IllegalArgumentException {
+ if (sibling == null) {
+ this.anchorElement = null;
+ this.insertionPolicy = INSERT_LAST;
+ } else {
+ this.anchorElement = sibling;
+ this.insertionPolicy = policy;
+ }
+ }
+
+ /*
+ * Generates a new AST for this operation and applies it to the given cu
+ */
+ protected void generateNewRubyScriptAST(IRubyScript cu) throws RubyModelException {
+ this.cuAST = parse(cu);
+ IDocument document = getDocument(cu);
+ ASTRewrite rewriter = ASTRewrite.create(this.cuAST, document);
+
+ Node child = generateElementAST(document, cu);
+ if (child != null) {
+ Node parent = ((RubyElement) getParentElement()).findNode(this.cuAST);
+ if (parent == null)
+ parent = this.cuAST;
+ insertASTNode(rewriter, parent, child);
+ apply(rewriter, document);
+ }
+ worked(1);
+ }
+
+ private void insertASTNode(ASTRewrite rewriter, Node parent, Node child) {
+ switch (this.insertionPolicy) {
+ case INSERT_BEFORE:
+ Node element = ((RubyElement) this.anchorElement).findNode(this.cuAST);
+ rewriter.insertBefore(source, child, element, null);
+ case INSERT_AFTER:
+ element = ((RubyElement) this.anchorElement).findNode(this.cuAST);
+ rewriter.insertAfter(source, child, element, null);
+ case INSERT_LAST:
+ rewriter.insertLast(source, child, null);
+ break;
+ }
+ }
+
+ private Node generateElementAST(IDocument document, IRubyScript cu) {
+ RubyParser parser = new RubyParser();
+ Node root = parser.parse(this.source);
+ NewlineNode newline = (NewlineNode) root;
+ this.createdNode = newline.getNextNode(); // Grab node from our parsed source!
+ return this.createdNode;
+ }
+
+ protected Node parse(IRubyScript cu) throws RubyModelException {
+ // ensure cu is consistent (noop if already consistent)
+ cu.makeConsistent(this.progressMonitor);
+ // create an AST for the ruby script
+ RubyParser parser = new RubyParser();
+ return parser.parse(cu.getSource());
+ }
+
+ protected void apply(ASTRewrite rewriter, IDocument document) throws RubyModelException {
+ TextEdit edits = rewriter.rewriteAST(document, null);
+ try {
+ edits.apply(document);
+ } catch (BadLocationException e) {
+ throw new RubyModelException(e, IRubyModelStatusConstants.INVALID_CONTENTS);
+ }
+ }
+
+ /**
+ * Possible failures: <ul>
+ * <li>NO_ELEMENTS_TO_PROCESS - the compilation unit supplied to the operation is
+ * <code>null</code>.
+ * <li>INVALID_NAME - no name, a name was null or not a valid
+ * import declaration name.
+ * <li>INVALID_SIBLING - the sibling provided for positioning is not valid.
+ * </ul>
+ * @see IRubyModelStatus
+ */
+ public IRubyModelStatus verify() {
+ if (getParentElement() == null) {
+ return new RubyModelStatus(IRubyModelStatusConstants.NO_ELEMENTS_TO_PROCESS);
+ }
+ if (this.anchorElement != null) {
+ IRubyElement domPresentParent = this.anchorElement.getParent();
+ if (!domPresentParent.equals(getParentElement())) {
+ return new RubyModelStatus(IRubyModelStatusConstants.INVALID_SIBLING, this.anchorElement);
+ }
+ }
+ return RubyModelStatus.VERIFIED_OK;
+ }
+
+}
Added: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/CreateRubyScriptOperation.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/CreateRubyScriptOperation.java (rev 0)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/CreateRubyScriptOperation.java 2006-12-15 15:31:22 UTC (rev 1705)
@@ -0,0 +1,173 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.rubypeople.rdt.internal.core;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.rubypeople.rdt.core.IBuffer;
+import org.rubypeople.rdt.core.IRubyElement;
+import org.rubypeople.rdt.core.IRubyElementDelta;
+import org.rubypeople.rdt.core.IRubyModelStatus;
+import org.rubypeople.rdt.core.IRubyModelStatusConstants;
+import org.rubypeople.rdt.core.IRubyScript;
+import org.rubypeople.rdt.core.ISourceFolder;
+import org.rubypeople.rdt.core.RubyConventions;
+import org.rubypeople.rdt.core.RubyModelException;
+import org.rubypeople.rdt.internal.core.util.Messages;
+import org.rubypeople.rdt.internal.core.util.Util;
+
+/**
+ * <p>This operation creates a compilation unit (CU).
+ * If the CU doesn't exist yet, a new compilation unit will be created with the content provided.
+ * Otherwise the operation will override the contents of an existing CU with the new content.
+ *
+ * <p>Note: It is possible to create a CU automatically when creating a
+ * class or interface. Thus, the preferred method of creating a CU is
+ * to perform a create type operation rather than
+ * first creating a CU and secondly creating a type inside the CU.
+ *
+ * <p>Required Attributes:<ul>
+ * <li>The package fragment in which to create the compilation unit.
+ * <li>The name of the compilation unit.
+ * Do not include the <code>".java"</code> suffix (ex. <code>"Object"</code> -
+ * the <code>".java"</code> will be added for the name of the compilation unit.)
+ * <li>
+ * </ul>
+ */
+public class CreateRubyScriptOperation extends RubyModelOperation {
+
+ /**
+ * The name of the compilation unit being created.
+ */
+ protected String fName;
+ /**
+ * The source code to use when creating the element.
+ */
+ protected String fSource= null;
+/**
+ * When executed, this operation will create a compilation unit with the given name.
+ * The name should have the ".java" suffix.
+ */
+public CreateRubyScriptOperation(ISourceFolder parentElement, String name, String source, boolean force) {
+ super(null, new IRubyElement[] {parentElement}, force);
+ fName = name;
+ fSource = source;
+}
+/**
+ * Creates a compilation unit.
+ *
+ * @exception RubyModelException if unable to create the compilation unit.
+ */
+protected void executeOperation() throws RubyModelException {
+ try {
+ beginTask(Messages.operation_createUnitProgress, 2);
+ RubyElementDelta delta = newRubyElementDelta();
+ IRubyScript unit = getRubyScript();
+ ISourceFolder pkg = (ISourceFolder) getParentElement();
+ IContainer folder = (IContainer) pkg.getResource();
+ worked(1);
+ IFile compilationUnitFile = folder.getFile(new Path(fName));
+ if (compilationUnitFile.exists()) {
+ // update the contents of the existing unit if fForce is true
+ if (force) {
+ IBuffer buffer = unit.getBuffer();
+ if (buffer == null) return;
+ buffer.setContents(fSource);
+ unit.save(new NullProgressMonitor(), false);
+ resultElements = new IRubyElement[] {unit};
+ if (!Util.isExcluded(unit)
+ && unit.getParent().exists()) {
+ for (int i = 0; i < resultElements.length; i++) {
+ delta.changed(resultElements[i], IRubyElementDelta.F_CONTENT);
+ }
+ addDelta(delta);
+ }
+ } else {
+ throw new RubyModelException(new RubyModelStatus(
+ IRubyModelStatusConstants.NAME_COLLISION,
+ Messages.bind(Messages.status_nameCollision, compilationUnitFile.getFullPath().toString())));
+ }
+ } else {
+ try {
+ String encoding = null;
+ try {
+ encoding = folder.getDefaultCharset(); // get folder encoding as file is not accessible
+ }
+ catch (CoreException ce) {
+ // use no encoding
+ }
+ InputStream stream = new ByteArrayInputStream(encoding == null ? fSource.getBytes() : fSource.getBytes(encoding));
+ createFile(folder, unit.getElementName(), stream, force);
+ resultElements = new IRubyElement[] {unit};
+ if (!Util.isExcluded(unit)
+ && unit.getParent().exists()) {
+ for (int i = 0; i < resultElements.length; i++) {
+ delta.added(resultElements[i]);
+ }
+ addDelta(delta);
+ }
+ } catch (IOException e) {
+ throw new RubyModelException(e, IRubyModelStatusConstants.IO_EXCEPTION);
+ }
+ }
+ worked(1);
+ } finally {
+ done();
+ }
+}
+/**
+ * @see CreateElementInCUOperation#getCompilationUnit()
+ */
+protected IRubyScript getRubyScript() {
+ return ((ISourceFolder)getParentElement()).getRubyScript(fName);
+}
+protected ISchedulingRule getSchedulingRule() {
+ IResource resource = getRubyScript().getResource();
+ IWorkspace workspace = resource.getWorkspace();
+ if (resource.exists()) {
+ return workspace.getRuleFactory().modifyRule(resource);
+ } else {
+ return workspace.getRuleFactory().createRule(resource);
+ }
+}
+/**
+ * Possible failures: <ul>
+ * <li>NO_ELEMENTS_TO_PROCESS - the package fragment supplied to the operation is
+ * <code>null</code>.
+ * <li>INVALID_NAME - the compilation unit name provided to the operation
+ * is <code>null</code> or has an invalid syntax
+ * <li>INVALID_CONTENTS - the source specified for the compiliation unit is null
+ * </ul>
+ */
+public IRubyModelStatus verify() {
+ if (getParentElement() == null) {
+ return new RubyModelStatus(IRubyModelStatusConstants.NO_ELEMENTS_TO_PROCESS);
+ }
+ if (RubyConventions.validateRubyScriptName(fName).getSeverity() == IStatus.ERROR) {
+ return new RubyModelStatus(IRubyModelStatusConstants.INVALID_NAME, fName);
+ }
+ if (fSource == null) {
+ return new RubyModelStatus(IRubyModelStatusConstants.INVALID_CONTENTS);
+ }
+ return RubyModelStatus.VERIFIED_OK;
+}
+}
Added: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/CreateSourceFolderOperation.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/CreateSourceFolderOperation.java (rev 0)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/CreateSourceFolderOperation.java 2006-12-15 15:31:22 UTC (rev 1705)
@@ -0,0 +1,146 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.rubypeople.rdt.internal.core;
+
+import java.util.ArrayList;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.rubypeople.rdt.core.IRubyElement;
+import org.rubypeople.rdt.core.IRubyModelStatus;
+import org.rubypeople.rdt.core.IRubyModelStatusConstants;
+import org.rubypeople.rdt.core.IRubyProject;
+import org.rubypeople.rdt.core.ISourceFolder;
+import org.rubypeople.rdt.core.RubyConventions;
+import org.rubypeople.rdt.core.RubyModelException;
+import org.rubypeople.rdt.internal.core.util.CharOperation;
+import org.rubypeople.rdt.internal.core.util.Messages;
+import org.rubypeople.rdt.internal.core.util.Util;
+
+/**
+ * This operation creates a new package fragment under a given package fragment root.
+ * The following must be specified: <ul>
+ * <li>the package fragment root
+ * <li>the package name
+ * </ul>
+ * <p>Any needed folders/package fragments are created.
+ * If the package fragment already exists, this operation has no effect.
+ * The result elements include the <code>IPackageFragment</code> created and any side effect
+ * package fragments that were created.
+ *
+ * <p>NOTE: A default package fragment exists by default for a given root.
+ *
+ * <p>Possible exception conditions: <ul>
+ * <li>Package fragment root is read-only
+ * <li>Package fragment's name is taken by a simple (non-folder) resource
+ * </ul>
+ */
+public class CreateSourceFolderOperation extends RubyModelOperation {
+ /**
+ * The fully qualified, dot-delimited, package name.
+ */
+ protected String[] pkgName;
+/**
+ * When executed, this operation will create a package fragment with the given name
+ * under the given package fragment root. The dot-separated name is broken into
+ * segments. Intermediate folders are created as required for each segment.
+ * If the folders already exist, this operation has no effect.
+ */
+public CreateSourceFolderOperation(IRubyProject parentElement, String packageName, boolean force) {
+ super(null, new IRubyElement[]{parentElement}, force);
+ this.pkgName = packageName == null ? null : Util.getTrimmedSimpleNames(packageName);
+}
+/**
+ * Execute the operation - creates the new package fragment and any
+ * side effect package fragments.
+ *
+ * @exception RubyModelException if the operation is unable to complete
+ */
+protected void executeOperation() throws RubyModelException {
+ RubyElementDelta delta = null;
+ RubyProject root = (RubyProject) getParentElement();
+ beginTask(Messages.operation_createPackageFragmentProgress, this.pkgName.length);
+ IContainer parentFolder = (IContainer) root.getResource();
+ String[] sideEffectPackageName = CharOperation.NO_STRINGS;
+ ArrayList results = new ArrayList(this.pkgName.length);
+ int i;
+ for (i = 0; i < this.pkgName.length; i++) {
+ String subFolderName = this.pkgName[i];
+ sideEffectPackageName = Util.arrayConcat(sideEffectPackageName, subFolderName);
+ IResource subFolder = parentFolder.findMember(subFolderName);
+ if (subFolder == null) {
+ createFolder(parentFolder, subFolderName, force);
+ parentFolder = parentFolder.getFolder(new Path(subFolderName));
+ ISourceFolder addedFrag = root.getSourceFolder(sideEffectPackageName);
+ if (delta == null) {
+ delta = newRubyElementDelta();
+ }
+ delta.added(addedFrag);
+ results.add(addedFrag);
+ } else {
+ parentFolder = (IContainer) subFolder;
+ }
+ worked(1);
+ }
+ if (results.size() > 0) {
+ this.resultElements = new IRubyElement[results.size()];
+ results.toArray(this.resultElements);
+ if (delta != null) {
+ addDelta(delta);
+ }
+ }
+ done();
+}
+/**
+ * Possible failures: <ul>
+ * <li>NO_ELEMENTS_TO_PROCESS - the root supplied to the operation is
+ * <code>null</code>.
+ * <li>INVALID_NAME - the name provided to the operation
+ * is <code>null</code> or is not a valid package fragment name.
+ * <li>READ_ONLY - the root provided to this operation is read only.
+ * <li>NAME_COLLISION - there is a pre-existing resource (file)
+ * with the same name as a folder in the package fragment's hierarchy.
+ * <li>ELEMENT_NOT_PRESENT - the underlying resource for the root is missing
+ * </ul>
+ * @see IRubyModelStatus
+ * @see RubyConventions
+ */
+public IRubyModelStatus verify() {
+ if (getParentElement() == null) {
+ return new RubyModelStatus(IRubyModelStatusConstants.NO_ELEMENTS_TO_PROCESS);
+ }
+
+ String packageName = this.pkgName == null ? null : Util.concatWith(this.pkgName, '/');
+ if (this.pkgName == null) {
+ return new RubyModelStatus(IRubyModelStatusConstants.INVALID_NAME, packageName);
+ }
+ IRubyProject root = (IRubyProject) getParentElement();
+ if (root.isReadOnly()) {
+ return new RubyModelStatus(IRubyModelStatusConstants.READ_ONLY, root);
+ }
+ IContainer parentFolder = (IContainer) root.getResource();
+ int i;
+ for (i = 0; i < this.pkgName.length; i++) {
+ IResource subFolder = parentFolder.findMember(this.pkgName[i]);
+ if (subFolder != null) {
+ if (subFolder.getType() != IResource.FOLDER) {
+ return new RubyModelStatus(
+ IRubyModelStatusConstants.NAME_COLLISION,
+ Messages.bind(Messages.status_nameCollision, subFolder.getFullPath().toString()));
+ }
+ parentFolder = (IContainer) subFolder;
+ }
+ }
+ return RubyModelStatus.VERIFIED_OK;
+}
+}
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/Openable.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/Openable.java 2006-12-15 15:29:19 UTC (rev 1704)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/Openable.java 2006-12-15 15:31:22 UTC (rev 1705)
@@ -10,6 +10,8 @@
import java.util.Map;
import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.rubypeople.rdt.core.BufferChangedEvent;
@@ -339,6 +341,19 @@
}
/**
+ * Returns whether the corresponding resource or associated file exists
+ */
+ protected boolean resourceExists() {
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ if (workspace == null) return false; // workaround for http://bugs.eclipse.org/bugs/show_bug.cgi?id=34069
+ return
+ RubyModel.getTarget(
+ workspace.getRoot(),
+ this.getPath().makeRelative(), // ensure path is relative (see http://dev.eclipse.org/bugs/show_bug.cgi?id=22517)
+ true) != null;
+ }
+
+ /**
* @see IOpenable
*/
public void open(IProgressMonitor pm) throws RubyModelException {
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyElement.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyElement.java 2006-12-15 15:29:19 UTC (rev 1704)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyElement.java 2006-12-15 15:31:22 UTC (rev 1705)
@@ -30,6 +30,7 @@
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.PlatformObject;
+import org.jruby.ast.Node;
import org.rubypeople.rdt.core.IField;
import org.rubypeople.rdt.core.IOpenable;
import org.rubypeople.rdt.core.IParent;
@@ -524,4 +525,8 @@
protected void toStringName(StringBuffer buffer) {
buffer.append(getElementName());
}
+
+ public Node findNode(Node cuAST) {
+ return null; // works only inside a ruby script
+ }
}
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyModel.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyModel.java 2006-12-15 15:29:19 UTC (rev 1704)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyModel.java 2006-12-15 15:31:22 UTC (rev 1705)
@@ -4,9 +4,12 @@
*/
package org.rubypeople.rdt.internal.core;
+import java.io.File;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.Map;
+import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
@@ -28,9 +31,31 @@
*/
public class RubyModel extends Openable implements IRubyModel {
+ /**
+ * A set of java.io.Files used as a cache of external jars that
+ * are known to be existing.
+ * Note this cache is kept for the whole session.
+ */
+ public static HashSet existingExternalFiles = new HashSet();
+
+ /**
+ * A set of external files ({@link #existingExternalFiles}) which have
+ * been confirmed as file (ie. which returns true to {@link java.io.File#isFile()}.
+ * Note this cache is kept for the whole session.
+ */
+ public static HashSet existingExternalConfirmedFiles = new HashSet();
+
protected RubyModel() {
super(null);
}
+
+ /**
+ * Flushes the cache of external files known to be existing.
+ */
+ public static void flushExternalFileCache() {
+ existingExternalFiles = new HashSet();
+ existingExternalConfirmedFiles = new HashSet();
+ }
/**
* Returns a new element info for this element.
@@ -118,11 +143,7 @@
}
- protected boolean buildStructure(OpenableElementInfo info, IProgressMonitor pm, Map newElements, IResource underlyingResource) /*
- * throws
- * RubyModelException
- */{
-
+ protected boolean buildStructure(OpenableElementInfo info, IProgressMonitor pm, Map newElements, IResource underlyingResource) {
// determine my children
IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
for (int i = 0, max = projects.length; i < max; i++) {
@@ -163,4 +184,50 @@
return new RubyProject(ResourcesPlugin.getWorkspace().getRoot().getProject(projectName), this);
}
+ /**
+ * Helper method - returns the targeted item (IResource if internal or java.io.File if external),
+ * or null if unbound
+ * Internal items must be referred to using container relative paths.
+ */
+public static Object getTarget(IContainer container, IPath path, boolean checkResourceExistence) {
+
+ if (path == null) return null;
+
+ // lookup - inside the container
+ if (path.getDevice() == null) { // container relative paths should not contain a device
+ // (see http://dev.eclipse.org/bugs/show_bug.cgi?id=18684)
+ // (case of a workspace rooted at d:\ )
+ IResource resource = container.findMember(path);
+ if (resource != null){
+ if (!checkResourceExistence ||resource.exists()) return resource;
+ return null;
+ }
+ }
+
+ // if path is relative, it cannot be an external path
+ // (see http://dev.eclipse.org/bugs/show_bug.cgi?id=22517)
+ if (!path.isAbsolute()) return null;
+
+ // lookup - outside the container
+ return getTargetAsExternalFile(path, checkResourceExistence);
}
+private synchronized static Object getTargetAsExternalFile(IPath path, boolean checkResourceExistence) {
+ File externalFile = new File(path.toOSString());
+ if (!checkResourceExistence) {
+ return externalFile;
+ } else if (existingExternalFiles.contains(externalFile)) {
+ return externalFile;
+ } else {
+ if (RubyModelManager.ZIP_ACCESS_VERBOSE) {
+ System.out.println("(" + Thread.currentThread() + ") [RubyModel.getTarget(...)] Checking existence of " + path.toString()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ if (externalFile.exists()) {
+ // cache external file
+ existingExternalFiles.add(externalFile);
+ return externalFile;
+ }
+ }
+ return null;
+}
+
+}
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyModelManager.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyModelManager.java 2006-12-15 15:29:19 UTC (rev 1704)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyModelManager.java 2006-12-15 15:31:22 UTC (rev 1705)
@@ -42,6 +42,7 @@
import org.rubypeople.rdt.internal.core.buffer.BufferManager;
import org.rubypeople.rdt.internal.core.builder.RubyBuilder;
import org.rubypeople.rdt.internal.core.util.Util;
+import org.rubypeople.rdt.internal.core.util.WeakHashSet;
/**
* @author cawilliams
@@ -113,14 +114,18 @@
private static boolean verbose = false;
public static boolean CP_RESOLVE_VERBOSE = false;
+ public static boolean ZIP_ACCESS_VERBOSE = false;
// Preferences
HashSet optionNames = new HashSet(20);
Hashtable optionsCache;
public final IEclipsePreferences[] preferencesLookup = new IEclipsePreferences[2];
+ private WeakHashSet stringSymbols = new WeakHashSet(5);
static final int PREF_INSTANCE = 0;
static final int PREF_DEFAULT = 1;
+
+ public static final IRubyScript[] NO_WORKING_COPY = new IRubyScript[0];
/**
* Update the classpath variable cache
@@ -740,7 +745,9 @@
case IResource.FILE :
return create((IFile) resource, project);
case IResource.FOLDER :
+// System.err.println("Tried to create a RubyElement for: " + resource.getFullPath().toOSString());
return create((IFolder) resource, project);
+// return null;
case IResource.ROOT :
return RubyCore.create((IWorkspaceRoot) resource);
default :
@@ -748,6 +755,68 @@
}
}
+ /**
+ * Returns the package fragment or package fragment root corresponding to the given folder,
+ * its parent or great parent being the given project.
+ * or <code>null</code> if unable to associate the given folder with a Java element.
+ * <p>
+ * Note that a package fragment root is returned rather than a default package.
+ * <p>
+ * Creating a Java element has the side effect of creating and opening all of the
+ * element's parents if they are not yet open.
+ */
+ public static IRubyElement create(IFolder folder, IRubyProject project) {
+ if (folder == null) {
+ return null;
+ }
+ IRubyElement element;
+ if (project == null) {
+ project = RubyCore.create(folder.getProject());
+
+ element = determineIfOnLoadpath(folder, project);
+ if (element == null) {
+ // walk all projects and find one that have the given folder on the load path
+ IRubyProject[] projects;
+ try {
+ projects = RubyModelManager.getRubyModelManager().getRubyModel().getRubyProjects();
+ } catch (RubyModelException e) {
+ return null;
+ }
+ for (int i = 0, length = projects.length; i < length; i++) {
+ project = projects[i];
+ element = determineIfOnLoadpath(folder, project);
+ if (element != null)
+ break;
+ }
+ }
+ } else {
+ element = determineIfOnLoadpath(folder, project);
+ }
+
+ return element;
+ }
+
+ private static IRubyElement determineIfOnLoadpath(IResource resource,
+ IRubyProject project) {
+ // TODO Actually take load paths into account
+ IPath resourcePath = resource.getFullPath();
+ IPath rootPath = project.getPath();
+ if (rootPath.equals(resourcePath)) {
+ return project.getSourceFolder(resource);
+ } else if (rootPath.isPrefixOf(resourcePath)) {
+ IPath pkgPath = resourcePath.removeFirstSegments(rootPath.segmentCount());
+
+ if (resource.getType() == IResource.FILE) {
+ // if the resource is a file, then remove the last segment which
+ // is the file name in the package
+ pkgPath = pkgPath.removeLastSegments(1);
+ }
+ String[] pkgName = pkgPath.segments();
+ return project.getSourceFolder(pkgName);
+ }
+ return null;
+ }
+
public static IRubyElement create(IFile file, IRubyProject project) {
if (file == null) {
return null;
@@ -772,5 +841,57 @@
}
return project.getRubyScript(file);
}
+
+ /*
+ * Returns all the working copies which have the given owner.
+ * Adds the working copies of the primary owner if specified.
+ * Returns null if it has none.
+ */
+ public IRubyScript[] getWorkingCopies(WorkingCopyOwner owner, boolean addPrimary) {
+ synchronized(this.perWorkingCopyInfos) {
+ IRubyScript[] primaryWCs = addPrimary && owner != DefaultWorkingCopyOwner.PRIMARY
+ ? getWorkingCopies(DefaultWorkingCopyOwner.PRIMARY, false)
+ : null;
+ Map workingCopyToInfos = (Map)this.perWorkingCopyInfos.get(owner);
+ if (workingCopyToInfos == null) return primaryWCs;
+ int primaryLength = primaryWCs == null ? 0 : primaryWCs.length;
+ int size = workingCopyToInfos.size(); // note size is > 0 otherwise pathToPerWorkingCopyInfos would be null
+ IRubyScript[] result = new IRubyScript[primaryLength + size];
+ int index = 0;
+ if (primaryWCs != null) {
+ for (int i = 0; i < primaryLength; i++) {
+ IRubyScript primaryWorkingCopy = primaryWCs[i];
+ IRubyScript workingCopy = new RubyScript((SourceFolder) primaryWorkingCopy.getParent(), (IFile) primaryWorkingCopy.getResource(), primaryWorkingCopy.getElementName(), owner);
+ if (!workingCopyToInfos.containsKey(workingCopy))
+ result[index++] = primaryWorkingCopy;
+ }
+ if (index != primaryLength)
+ System.arraycopy(result, 0, result = new IRubyScript[index+size], 0, index);
+ }
+ Iterator iterator = workingCopyToInfos.values().iterator();
+ while(iterator.hasNext()) {
+ result[index++] = ((RubyModelManager.PerWorkingCopyInfo)iterator.next()).getWorkingCopy();
+ }
+ return result;
+ }
+ }
+ public synchronized String intern(String s) {
+ // make sure to copy the string (so that it doesn't hold on the underlying char[] that might be much bigger than necessary)
+ return (String) this.stringSymbols.add(new String(s));
+
+ // Note1: String#intern() cannot be used as on some VMs this prevents the string from being garbage collected
+ // Note 2: Instead of using a WeakHashset, one could use a WeakHashMap with the following implementation
+ // This would costs more per entry (one Entry object and one WeakReference more))
+
+ /*
+ WeakReference reference = (WeakReference) this.symbols.get(s);
+ String existing;
+ if (reference != null && (existing = (String) reference.get()) != null)
+ return existing;
+ this.symbols.put(s, new WeakReference(s));
+ return s;
+ */
+ }
+
}
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyModelOperation.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyModelOperation.java 2006-12-15 15:29:19 UTC (rev 1704)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyModelOperation.java 2006-12-15 15:31:22 UTC (rev 1705)
@@ -382,7 +382,7 @@
* or the element itself (if it is a compilation unit),
* otherwise <code>null</code>.
*/
- protected IRubyScript getRubyScripttFor(IRubyElement element) {
+ protected IRubyScript getRubyScriptFor(IRubyElement element) {
return ((RubyElement)element).getRubyScript();
}
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyProject.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyProject.java 2006-12-15 15:29:19 UTC (rev 1704)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyProject.java 2006-12-15 15:31:22 UTC (rev 1705)
@@ -16,6 +16,7 @@
import javax.xml.parsers.SAXParserFactory;
import org.eclipse.core.resources.ICommand;
+import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
@@ -33,12 +34,16 @@
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.IRubyProject;
import org.rubypeople.rdt.core.IRubyScript;
+import org.rubypeople.rdt.core.ISourceFolder;
import org.rubypeople.rdt.core.IType;
import org.rubypeople.rdt.core.RubyCore;
import org.rubypeople.rdt.core.RubyModelException;
import org.rubypeople.rdt.internal.core.builder.ProjectFileFinder;
+import org.rubypeople.rdt.internal.core.util.CharOperation;
+import org.rubypeople.rdt.internal.core.util.Util;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
@@ -84,7 +89,14 @@
public boolean upgrade() throws CoreException {
return addToBuildSpec(RubyCore.BUILDER_ID);
}
+
+ public ISourceFolder createSourceFolder(String pkgName, boolean force, IProgressMonitor monitor) throws RubyModelException {
+ CreateSourceFolderOperation op = new CreateSourceFolderOperation(this, pkgName, force);
+ op.runOperation(monitor);
+ return getSourceFolder(op.pkgName);
+ }
+
/**
* Adds a builder to the build spec for the given project.
*/
@@ -513,20 +525,95 @@
return false;
}
- /**
- * @see Openable
- */
- protected boolean buildStructure(OpenableElementInfo info, IProgressMonitor pm,
- Map newElements, IResource underlyingResource) throws RubyModelException {
- // check whether the ruby project can be opened
- if (!underlyingResource.isAccessible()) { throw newNotPresentException(); }
+ /**
+ * @see Openable
+ */
+ protected boolean buildStructure(OpenableElementInfo info, IProgressMonitor pm, Map newElements, IResource underlyingResource) throws RubyModelException {
+
+ // check whether the ruby project can be opened
+ if (!hasRubyNature((IProject) underlyingResource)) {
+ throw newNotPresentException();
+ }
+ // check whether this project can be opened
+ if (!resourceExists()) throw newNotPresentException();
- // Find all the scripts in this project
- info.setChildren(getScripts());
- return true;
- }
+ return computeChildren(info, newElements);
+ }
+
+ /**
+ * Compute the package fragment children of this package fragment root.
+ *
+ * @exception JavaModelException The resource associated with this package fragment 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;
+ }
+
+ /**
+ * 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 {
+ // Don't add yourself as a child
+// ISourceFolder pkg = getSourceFolder(pkgName);
+// vChildren.add(pkg);
- /**
+ try {
+ RubyProject javaProject = (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 (javaProject.contains(member)) {
+ String[] newNames = Util.arrayConcat(pkgName, manager.intern(memberName));
+// computeFolderChildren((IFolder) member, newNames, vChildren);
+ ISourceFolder child = getSourceFolder(newNames);
+ vChildren.add(child);
+ }
+
+ 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 boolean contains(IResource resource) {
+ // XXX Check the paths to see if this is true or not!
+ return true;
+ }
+
+ /**
* @return
*/
private IRubyElement[] getScripts() {
@@ -727,12 +814,44 @@
}
public boolean isOnLoadpath(IRubyScript element) {
- // TODO We cheat and assume all scripts are on loadpath. Thsi may not be true. We shoudl check!
+ // TODO We cheat and assume all scripts are on loadpath. This may not be true. We should check!
return true;
}
public IRubyScript getRubyScript(IFile file) {
return new RubyScript(this, file, file.getName(), DefaultWorkingCopyOwner.PRIMARY);
}
+
+ public ISourceFolder getSourceFolder(String[] names) {
+ return new SourceFolder(this, names);
+ }
+
+ public ISourceFolder getSourceFolder(IResource resource) {
+ switch (resource.getType()) {
+ case IResource.FILE:
+ return null;
+ case IResource.FOLDER:
+ return new SourceFolder(this, resource.getProjectRelativePath().segments());
+ case IResource.PROJECT:
+ return new SourceFolder(this, resource.getProjectRelativePath().segments());
+ default:
+ return null;
+ }
+ }
+
+ public ISourceFolder[] getSourceFolders() throws RubyModelException {
+ Object[] children;
+ int length;
+ ISourceFolder[] roots;
+
+ System.arraycopy(
+ children = getChildren(),
+ 0,
+ roots = new ISourceFolder[length = children.length],
+ 0,
+ length);
+
+ return roots;
+ }
}
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyScriptStructureBuilder.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyScriptStructureBuilder.java 2006-12-15 15:29:19 UTC (rev 1704)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyScriptStructureBuilder.java 2006-12-15 15:31:22 UTC (rev 1705)
@@ -35,7 +35,6 @@
import org.jruby.ast.AndNode;
im...
[truncated message content] |