|
From: <caw...@us...> - 2007-03-22 19:29:58
|
Revision: 2211
http://svn.sourceforge.net/rubyeclipse/?rev=2211&view=rev
Author: cawilliams
Date: 2007-03-22 12:28:46 -0700 (Thu, 22 Mar 2007)
Log Message:
-----------
start adding a new extension based on JDT's compilationParticipant extnesion point. This lets plugins hook into the build and reconcile process to garb the AST, script, file, or contents and also add/mange problem and task markers on the resource/working copy.
Modified Paths:
--------------
trunk/org.rubypeople.rdt.core/plugin.xml
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/ReconcileWorkingCopyOperation.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyElementDeltaBuilder.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/RubyScript.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyScriptProblemFinder.java
Added Paths:
-----------
trunk/org.rubypeople.rdt.core/schema/compilationParticipant.exsd
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/compiler/BuildContext.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/compiler/CompilationParticipant.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/compiler/CompilationParticipantResult.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/compiler/ReconcileContext.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/ASTHolderCUInfo.java
Modified: trunk/org.rubypeople.rdt.core/plugin.xml
===================================================================
--- trunk/org.rubypeople.rdt.core/plugin.xml 2007-03-22 15:59:32 UTC (rev 2210)
+++ trunk/org.rubypeople.rdt.core/plugin.xml 2007-03-22 19:28:46 UTC (rev 2211)
@@ -3,6 +3,7 @@
<plugin>
<extension-point id="loadpathVariableInitializer" name="%loadpathVariableInitializersName" schema="schema/loadpathVariableInitializer.exsd"/>
<extension-point id="loadpathContainerInitializer" name="%loadpathContainerInitializersName" schema="schema/loadpathContainerInitializer.exsd"/>
+ <extension-point id="compilationParticipant" name="%compilationParticipantsName" schema="schema/compilationParticipant.exsd"/>
<extension
id="rubynature"
Added: trunk/org.rubypeople.rdt.core/schema/compilationParticipant.exsd
===================================================================
--- trunk/org.rubypeople.rdt.core/schema/compilationParticipant.exsd (rev 0)
+++ trunk/org.rubypeople.rdt.core/schema/compilationParticipant.exsd 2007-03-22 19:28:46 UTC (rev 2211)
@@ -0,0 +1,191 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.rubypeople.rdt.core">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="org.rubypeople.rdt.core" id="compilationParticipant" name="Compilation Participants"/>
+ </appInfo>
+ <documentation>
+ This extension point allows clients to participate in the compilation process by receiving notifications at various stages of build and reconcile, via a org.rubypeople.rdt.core.compiler.CompilationParticipant.
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <complexType>
+ <sequence>
+ <element ref="compilationParticipant" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute translatable="true"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="compilationParticipant">
+ <annotation>
+ <documentation>
+ definition of a compilation participant. This definition allows to order participants. Participants are run by group. The group of participants that modify the environment is run first, then the group of participants that create problems is run, finally the group of other participants is run. Inside each group, participants are ordered using their 'requires' attributes. If a 'requires' attribute point to a participant that doesn't belong to the group, it is ignored.
+ </documentation>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element ref="requires" minOccurs="0" maxOccurs="unbounded"/>
+ <element ref="managedMarker" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+ the class that implements this compilation participant. This class must implement a public subclass of <code>org.eclipse.jdt.core.compiler.CompilationParticipant</code> with a public 0-argument constructor.
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn="org.eclipse.jdt.core.compiler.CompilationParticipant"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+ a unique identifier for this participant
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="modifiesEnvironment" type="boolean">
+ <annotation>
+ <documentation>
+ optionally specify whether this compilation participant modifies the environment, thus affecting binding resolution. If not specified, false is assumed.
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="createsProblems" type="boolean">
+ <annotation>
+ <documentation>
+ optionally specifies whether this compilation participant creates problems. If not specified, false is assumed.
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="requiredSourceLevel" type="string">
+ <annotation>
+ <documentation>
+ the required source level this participant needs (one of the JavaCore.VERSION* constants). If a project's source level is below this required source level, the participant will not be invoked. If this attribute is not specified, the participant will be invoked for any source level.
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="requires">
+ <annotation>
+ <documentation>
+ a participant that is required to run this compilation participant
+ </documentation>
+ </annotation>
+ <complexType>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+ the unique identifier of the participant that is required
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="managedMarker">
+ <annotation>
+ <documentation>
+ a managed marker is created from the recorded problems (see BuildContext#recordNewProblems(CategorizedProblem[])) and is removed when the corresponding source file is re-built or when the project is cleaned
+ </documentation>
+ </annotation>
+ <complexType>
+ <attribute name="markerType" type="string" use="required">
+ <annotation>
+ <documentation>
+ the type of the managed marker
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="since"/>
+ </appInfo>
+ <documentation>
+ 0.9.0
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="examples"/>
+ </appInfo>
+ <documentation>
+ Example of a declaration of a <code>compilationParticipant</code>: <pre>
+<extension
+ id="apt"
+ name="%annotationProcessingName"
+ point="org.rubypeoplee.rdt.core.compilationParticipant">
+ <compilationParticipant
+ class="org.eclipse.jdt.apt.core.internal.AptCompilationParticipant"
+ id="APT">
+ <managedMarker markerType="org.rubypeople.rdt.apt.core.compile.problem"/>
+ </compilationParticipant>
+</extension>
+</pre>
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="apiInfo"/>
+ </appInfo>
+ <documentation>
+
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="implementation"/>
+ </appInfo>
+ <documentation>
+
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="copyright"/>
+ </appInfo>
+ <documentation>
+ Copyright (c) 2006 BEA Systems, Inc and others.<br>
+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
+<a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>
+ </documentation>
+ </annotation>
+
+</schema>
Added: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/compiler/BuildContext.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/compiler/BuildContext.java (rev 0)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/compiler/BuildContext.java 2007-03-22 19:28:46 UTC (rev 2211)
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * Copyright (c) 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.core.compiler;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.rubypeople.rdt.internal.core.util.Util;
+
+/**
+ * The context of a build event that is notified to interested compilation
+ * participants when {@link CompilationParticipant#buildStarting(BuildContext[], boolean) a build is starting},
+ * or to annotations processors when {@link CompilationParticipant#processAnnotations(BuildContext[]) a source file has annotations}.
+ * <p>
+ * This class is not intended to be instanciated or subclassed by clients.
+ * </p>
+ * @since 0.9.0
+ */
+public class BuildContext extends CompilationParticipantResult {
+
+/**
+ * Creates a build context for the given source file.
+ * <p>
+ * This constructor is not intended to be called by clients.
+ * </p>
+ *
+ * @param sourceFile the source file being built
+ */
+public BuildContext(IFile resource) {
+ super(resource);
+}
+
+/**
+ * Returns the contents of the compilation unit.
+ *
+ * @return the contents of the compilation unit
+ */
+public char[] getContents() {
+ try {
+ return Util.getResourceContentsAsCharArray(this.resource);
+ } catch (CoreException e) {
+ throw new RuntimeException("Missing source file: " + this.resource);
+ }
+}
+
+/**
+ * Returns the <code>IFile</code> representing the compilation unit.
+ *
+ * @return the <code>IFile</code> representing the compilation unit
+ */
+public IFile getFile() {
+ return this.resource;
+}
+
+/**
+ * Returns whether the compilation unit contained any annotations when it was compiled.
+ *
+ * NOTE: This is only valid during {@link CompilationParticipant#processAnnotations(BuildContext[])}.
+ *
+ * @return whether the compilation unit contained any annotations when it was compiled
+ */
+public boolean hasAnnotations() {
+ return this.hasAnnotations; // only set during processAnnotations
+}
+
+/**
+ * Record new problems to report against this compilationUnit.
+ * Markers are persisted for these problems only for the declared managed marker type
+ * (see the 'compilationParticipant' extension point).
+ *
+ * @param newProblems the problems to report
+ */
+public void recordNewProblems(CategorizedProblem[] newProblems) {
+ int length2 = newProblems.length;
+ if (length2 == 0) return;
+
+ int length1 = this.problems == null ? 0 : this.problems.length;
+ CategorizedProblem[] merged = new CategorizedProblem[length1 + length2];
+ if (length1 > 0) // always make a copy even if currently empty
+ System.arraycopy(this.problems, 0, merged, 0, length1);
+ System.arraycopy(newProblems, 0, merged, length1, length2);
+ this.problems = merged;
+}
+
+}
Added: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/compiler/CompilationParticipant.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/compiler/CompilationParticipant.java (rev 0)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/compiler/CompilationParticipant.java 2007-03-22 19:28:46 UTC (rev 2211)
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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:
+ * mka...@be... - initial API as ICompilationParticipant
+ * IBM - changed from interface ICompilationParticipant to abstract class CompilationParticipant
+ * IBM - rewrote spec
+ *
+ *******************************************************************************/
+
+package org.rubypeople.rdt.core.compiler;
+
+import org.rubypeople.rdt.core.IRubyProject;
+
+/**
+ * A compilation participant is notified of events occuring during the compilation process.
+ * The compilation process not only involves generating .class files (i.e. building), it also involve
+ * cleaning the output directory, reconciling a working copy, etc.
+ * So the notified events are the result of a build action, a clean action, a reconcile operation
+ * (for a working copy), etc.
+ * <p>
+ * Clients wishing to participate in the compilation process must suclass this class, and implement
+ * {@link #isActive(IJavaProject)}, {@link #aboutToBuild(IJavaProject)},
+ * {@link #reconcile(ReconcileContext)}, etc.
+* </p><p>
+ * This class is intended to be subclassed by clients.
+ * </p>
+ * @since 3.2
+ */
+public abstract class CompilationParticipant {
+
+public static int READY_FOR_BUILD = 1;
+public static int NEEDS_FULL_BUILD = 2;
+
+/**
+ * Notifies this participant that a build is about to start and provides it the opportunity to
+ * create missing source folders for generated source files.
+ * Only sent to participants interested in the project.
+ * <p>
+ * Default is to return <code>READY_FOR_BUILD</code>.
+ * </p>
+ * @param project the project about to build
+ * @return READY_FOR_BUILD or NEEDS_FULL_BUILD
+ */
+public int aboutToBuild(IRubyProject project) {
+ return READY_FOR_BUILD;
+}
+
+/**
+ * Notifies this participant that a compile operation is about to start and provides it the opportunity to
+ * generate source files based on the source files about to be compiled.
+ * When isBatchBuild is true, then files contains all source files in the project.
+ * Only sent to participants interested in the current build project.
+ *
+ * @param files is an array of BuildContext
+ * @param isBatch identifies when the build is a batch build
+ */
+public void buildStarting(BuildContext[] files, boolean isBatch) {
+ // do nothing by default
+}
+
+/**
+ * Notifies this participant that a clean is about to start and provides it the opportunity to
+ * delete generated source files.
+ * Only sent to participants interested in the project.
+ * @param project the project about to be cleaned
+ */
+public void cleanStarting(IRubyProject project) {
+ // do nothing by default
+}
+
+/**
+ * Returns whether this participant is active for a given project.
+ * <p>
+ * Default is to return <code>false</code>.
+ * </p><p>
+ * For efficiency, participants that are not interested in the
+ * given project should return <code>false</code> for that project.
+ * </p>
+ * @param project the project to participate in
+ * @return whether this participant is active for a given project
+ */
+public boolean isActive(IRubyProject project) {
+ return false;
+}
+
+/**
+ * Returns whether this participant is interested in only Annotations.
+ * <p>
+ * Default is to return <code>false</code>.
+ * </p>
+ * @return whether this participant is interested in only Annotations.
+ */
+public boolean isAnnotationProcessor() {
+ return false;
+}
+
+/**
+ * Notifies this participant that a compile operation has found source files using Annotations.
+ * Only sent to participants interested in the current build project that answer true to isAnnotationProcessor().
+ * Each BuildContext was informed whether its source file currently hasAnnotations().
+ *
+ * @param files is an array of BuildContext
+ */
+public void processAnnotations(BuildContext[] files) {
+ // do nothing by default
+}
+
+/**
+ * Notifies this participant that a reconcile operation is happening. The participant can act on this reconcile
+ * operation by using the given context. Other participant can then see the result of this participation
+ * on this context.
+ * <p>
+ * Note that a participant should not modify the buffer of the working copy that is being reconciled.
+ * </p><p>
+ * Default is to do nothing.
+ * </p>
+ * @param context the reconcile context to act on
+ */
+public void reconcile(ReconcileContext context) {
+ // do nothing by default
+}
+
+}
Added: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/compiler/CompilationParticipantResult.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/compiler/CompilationParticipantResult.java (rev 0)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/compiler/CompilationParticipantResult.java 2007-03-22 19:28:46 UTC (rev 2211)
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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 - rewrote spec
+ *
+ *******************************************************************************/
+
+package org.rubypeople.rdt.core.compiler;
+
+import org.eclipse.core.resources.IFile;
+
+public class CompilationParticipantResult {
+ protected IFile resource;
+ protected boolean hasAnnotations; // only set during processAnnotations
+ protected IFile[] addedFiles; // added/changed generated source files that need to be compiled
+ protected IFile[] deletedFiles; // previously generated source files that should be deleted
+ protected CategorizedProblem[] problems; // new problems to report against this compilationUnit
+ protected String[] dependencies; // fully-qualified type names of any new dependencies, each name is of the form 'p1.p2.A.B'
+
+protected CompilationParticipantResult(IFile resource) {
+ this.resource = resource;
+ this.hasAnnotations = false;
+ this.addedFiles = null;
+ this.deletedFiles = null;
+ this.problems = null;
+ this.dependencies = null;
+}
+
+void reset(boolean detectedAnnotations) {
+ // called prior to processAnnotations
+ this.hasAnnotations = detectedAnnotations;
+ this.addedFiles = null;
+ this.deletedFiles = null;
+ this.problems = null;
+ this.dependencies = null;
+}
+
+public String toString() {
+ return this.resource.toString();
+}
+
+}
Added: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/compiler/ReconcileContext.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/compiler/ReconcileContext.java (rev 0)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/compiler/ReconcileContext.java 2007-03-22 19:28:46 UTC (rev 2211)
@@ -0,0 +1,163 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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:
+ * mka...@be... - initial API and implementation
+ * IBM - renamed from PreReconcileCompilationResult to ReconcileContext
+ * IBM - rewrote spec
+ *
+ *******************************************************************************/
+
+package org.rubypeople.rdt.core.compiler;
+
+import java.util.HashMap;
+
+import org.jruby.ast.RootNode;
+import org.rubypeople.rdt.core.IRubyElementDelta;
+import org.rubypeople.rdt.core.IRubyModelMarker;
+import org.rubypeople.rdt.core.IRubyScript;
+import org.rubypeople.rdt.core.RubyModelException;
+import org.rubypeople.rdt.internal.core.ReconcileWorkingCopyOperation;
+import org.rubypeople.rdt.internal.core.RubyScript;
+
+/**
+ * The context of a reconcile event that is notified to interested compilation
+ * participants while a reconcile operation is running.
+ * <p>
+ * A reconcile participant can get the AST for the reconcile-operation using
+ * {@link #getAST()}. If the participant modifies in any way the AST
+ * (either by modifying the source of the working copy, or modifying another entity
+ * that would result in different bindings for the AST), it is expected to reset the
+ * AST in the context using {@link #resetAST()}.
+ * </p><p>
+ * A reconcile participant can also create and return problems using
+ * {@link #putProblems(String, CategorizedProblem[])}. These problems are then reported
+ * to the problem requestor of the reconcile operation.
+ * </p><p>
+ * This class is not intended to be instanciated or subclassed by clients.
+ * </p>
+ *
+ * @see CompilationParticipant#reconcile(ReconcileContext)
+ * @since 0.9.0
+ */
+public class ReconcileContext {
+
+ private ReconcileWorkingCopyOperation operation;
+ private RubyScript workingCopy;
+
+/**
+ * Creates a reconcile context for the given reconcile operation.
+ * <p>
+ * This constructor is not intended to be called by clients.
+ * </p>
+ *
+ * @param operation the reconcile operation
+ */
+public ReconcileContext(ReconcileWorkingCopyOperation operation, RubyScript workingCopy) {
+ this.operation = operation;
+ this.workingCopy = workingCopy;
+}
+
+/**
+ * Returns a resolved AST with {@link AST#JLS3 JLS3} level.
+ * It is created from the current state of the working copy.
+ * Creates one if none exists yet.
+ * Returns <code>null</code> if the current state of the working copy
+ * doesn't allow the AST to be created (e.g. if the working copy's content
+ * cannot be parsed).
+ * <p>
+ * If the AST level requested during reconciling is not {@link AST#JLS3}
+ * or if binding resolutions was not requested, then a different AST is created.
+ * Note that this AST does not become the current AST and it is only valid for
+ * the requestor.
+ * </p>
+ *
+ * @return the AST created from the current state of the working copy,
+ * or <code>null</code> if none could be created
+ * @exception RubyModelException if the contents of the working copy
+ * cannot be accessed. Reasons include:
+ * <ul>
+ * <li> The working copy does not exist (ELEMENT_DOES_NOT_EXIST)</li>
+ * </ul>
+ */
+public RootNode getAST() throws RubyModelException {
+ return this.operation.makeConsistent(this.workingCopy, null/*don't report problems to the working copy's problem requestor*/);
+}
+
+
+/**
+ * Returns the delta describing the change to the working copy being reconciled.
+ * Returns <code>null</code> if there is no change.
+ * Note that the delta's AST is not yet positionnned at this stage. Use {@link #getAST3()}
+ * to get the current AST.
+ *
+ * @return the delta describing the change, or <code>null</code> if none
+ */
+public IRubyElementDelta getDelta() {
+ return this.operation.deltaBuilder.delta;
+}
+
+/**
+ * Returns the problems to be reported to the problem requestor of the reconcile operation
+ * for the given marker type.
+ * Returns <code>null</code> if no problems need to be reported for this marker type.
+ *
+ * @param markerType the given marker type
+ * @return problems to be reported to the problem requesto
+ */
+public CategorizedProblem[] getProblems(String markerType) {
+ if (this.operation.problems == null) return null;
+ return (CategorizedProblem[]) this.operation.problems.get(markerType);
+}
+
+/**
+ * Returns the working copy this context refers to.
+ *
+ * @return the working copy this context refers to
+ */
+public IRubyScript getWorkingCopy() {
+ return this.workingCopy;
+}
+
+/**
+ * Resets the AST carried by this context.
+ * A compilation participant that modifies the environment that would result in different
+ * bindings for the AST is expected to reset the AST on this context, so that other
+ * participants don't get a stale AST.
+ * <p>
+ * Note that resetting the AST will not restart the reconcile process. Only further
+ * participants will see the new AST. Thus participants running before the one that
+ * resets the AST will have a stale view of the AST and its problems. Use
+ * the compilation participant extension point to order the participants.
+ * </p>
+ */
+public void resetAST() {
+ this.operation.ast = null;
+ putProblems(IRubyModelMarker.RUBY_MODEL_PROBLEM_MARKER, null);
+ putProblems(IRubyModelMarker.TASK_MARKER, null);
+}
+
+/**
+ * Sets the problems to be reported to the problem requestor of the reconcile operation
+ * for the given marker type.
+ * <code>null</code> indicates that no problems need to be reported.
+ * <p>
+ * Using this functionality, a participant that resolves problems for a given marker type
+ * can hide those problems since they don't exist any longer.
+ * </p>
+ *
+ * @param markerType the marker type of the given problems
+ * @param problems the problems to be reported to the problem requestor of the reconcile operation,
+ * or <code>null</code> if none
+ */
+public void putProblems(String markerType, CategorizedProblem[] problems) {
+ if (this.operation.problems == null)
+ this.operation.problems = new HashMap();
+ this.operation.problems.put(markerType, problems);
+}
+
+}
Added: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/ASTHolderCUInfo.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/ASTHolderCUInfo.java (rev 0)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/ASTHolderCUInfo.java 2007-03-22 19:28:46 UTC (rev 2211)
@@ -0,0 +1,12 @@
+package org.rubypeople.rdt.internal.core;
+
+import java.util.HashMap;
+
+import org.jruby.ast.RootNode;
+
+public class ASTHolderCUInfo extends RubyScriptElementInfo {
+
+ public HashMap problems;
+ public RootNode ast;
+
+}
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/ReconcileWorkingCopyOperation.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/ReconcileWorkingCopyOperation.java 2007-03-22 15:59:32 UTC (rev 2210)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/ReconcileWorkingCopyOperation.java 2007-03-22 19:28:46 UTC (rev 2211)
@@ -10,109 +10,205 @@
*******************************************************************************/
package org.rubypeople.rdt.internal.core;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.SafeRunner;
+import org.jruby.ast.RootNode;
import org.rubypeople.rdt.core.IProblemRequestor;
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.RubyModelException;
import org.rubypeople.rdt.core.WorkingCopyOwner;
+import org.rubypeople.rdt.core.compiler.CategorizedProblem;
+import org.rubypeople.rdt.core.compiler.CompilationParticipant;
+import org.rubypeople.rdt.core.compiler.ReconcileContext;
import org.rubypeople.rdt.internal.core.util.Messages;
+import org.rubypeople.rdt.internal.core.util.Util;
/**
* Reconcile a working copy and signal the changes through a delta.
*/
public class ReconcileWorkingCopyOperation extends RubyModelOperation {
- public static boolean PERF = false;
- boolean createAST;
- boolean forceProblemDetection;
- WorkingCopyOwner workingCopyOwner;
- RubyScript ast;
+ public static boolean PERF = false;
+ boolean createAST;
+ boolean forceProblemDetection;
+ WorkingCopyOwner workingCopyOwner;
+ public RootNode ast;
+ public RubyElementDeltaBuilder deltaBuilder;
+ public HashMap problems;
- public ReconcileWorkingCopyOperation(IRubyElement workingCopy,
- boolean forceProblemDetection, WorkingCopyOwner workingCopyOwner) {
- super(new IRubyElement[] { workingCopy});
- this.forceProblemDetection = forceProblemDetection;
- this.workingCopyOwner = workingCopyOwner;
- }
+ public ReconcileWorkingCopyOperation(IRubyElement workingCopy, boolean forceProblemDetection, WorkingCopyOwner workingCopyOwner) {
+ super(new IRubyElement[] { workingCopy });
+ this.forceProblemDetection = forceProblemDetection;
+ this.workingCopyOwner = workingCopyOwner;
+ }
- /**
- * @exception RubyModelException
- * if setting the source of the original compilation unit
- * fails
- */
- protected void executeOperation() throws RubyModelException {
- if (this.progressMonitor != null) {
- if (this.progressMonitor.isCanceled()) throw new OperationCanceledException();
- this.progressMonitor.beginTask(Messages.bind(Messages.element_reconciling), 2);
- }
- RubyScript workingCopy = getWorkingCopy();
- boolean wasConsistent = workingCopy.isConsistent();
- try {
- if (!wasConsistent) {
- // create the delta builder (this remembers the current content
- // of the cu)
- RubyElementDeltaBuilder deltaBuilder = new RubyElementDeltaBuilder(workingCopy);
+ /**
+ * @exception RubyModelException
+ * if setting the source of the original compilation unit
+ * fails
+ */
+ protected void executeOperation() throws RubyModelException {
+ if (this.progressMonitor != null) {
+ if (this.progressMonitor.isCanceled())
+ throw new OperationCanceledException();
+ this.progressMonitor.beginTask(Messages.element_reconciling, 2);
+ }
- workingCopy.makeConsistent(this.progressMonitor);
- deltaBuilder.buildDeltas();
+ RubyScript workingCopy = getWorkingCopy();
+ boolean wasConsistent = workingCopy.isConsistent();
+ IProblemRequestor problemRequestor = workingCopy.getPerWorkingCopyInfo();
- if (progressMonitor != null) progressMonitor.worked(2);
+ // create the delta builder (this remembers the current content of the
+ // cu)
+ this.deltaBuilder = new RubyElementDeltaBuilder(workingCopy);
- // register the deltas
- RubyElementDelta delta = deltaBuilder.delta;
- if (delta != null) {
- delta.changedAST(this.ast);
- addReconcileDelta(workingCopy, delta);
- }
- } else {
- // force problem detection? - if structure was consistent
- if (forceProblemDetection) {
- IProblemRequestor problemRequestor = workingCopy.getPerWorkingCopyInfo();
- boolean computeProblems = RubyProject.hasRubyNature(workingCopy
- .getRubyProject().getProject())
- && problemRequestor != null && problemRequestor.isActive();
- if (computeProblems) {
+ // make working copy consistent if needed and compute AST if needed
+ makeConsistent(workingCopy, problemRequestor);
- char[] contents = workingCopy.getContents();
- problemRequestor.beginReporting();
- RubyScriptProblemFinder.process(workingCopy, contents, problemRequestor,
- progressMonitor);
- problemRequestor.endReporting();
- if (progressMonitor != null) progressMonitor.worked(1);
- // TODO Create AST?
- }
- }
- }
- } finally {
- if (progressMonitor != null) progressMonitor.done();
- }
- }
+ // notify reconcile participants
+ notifyParticipants(workingCopy);
- /**
- * Returns the working copy this operation is working on.
- */
- protected RubyScript getWorkingCopy() {
- return (RubyScript) getElementToProcess();
- }
+ // recreate ast if needed
+ if (this.ast == null)
+ makeConsistent(workingCopy, problemRequestor);
- /**
- * @see RubyModelOperation#isReadOnly
- */
- public boolean isReadOnly() {
- return true;
- }
+ // report problems
+ if (this.problems != null && (this.forceProblemDetection || !wasConsistent)) {
+ try {
+ problemRequestor.beginReporting();
+ for (Iterator iteraror = this.problems.values().iterator(); iteraror.hasNext();) {
+ CategorizedProblem[] categorizedProblems = (CategorizedProblem[]) iteraror.next();
+ if (categorizedProblems == null)
+ continue;
+ for (int i = 0, length = categorizedProblems.length; i < length; i++) {
+ CategorizedProblem problem = categorizedProblems[i];
+ if (RubyModelManager.VERBOSE) {
+ System.out.println("PROBLEM FOUND while reconciling : " + problem.getMessage());//$NON-NLS-1$
+ }
+ if (this.progressMonitor != null && this.progressMonitor.isCanceled())
+ break;
+ problemRequestor.acceptProblem(problem);
+ }
+ }
+ } finally {
+ problemRequestor.endReporting();
+ }
+ }
- protected IRubyModelStatus verify() {
- IRubyModelStatus status = super.verify();
- if (!status.isOK()) { return status; }
- RubyScript workingCopy = getWorkingCopy();
- if (!workingCopy.isWorkingCopy()) { return new RubyModelStatus(
- IRubyModelStatusConstants.ELEMENT_DOES_NOT_EXIST, workingCopy); // was
- // destroyed
- }
- return status;
- }
+ // report delta
+ try {
+ RubyElementDelta delta = this.deltaBuilder.delta;
+ if (delta != null) {
+ addReconcileDelta(workingCopy, delta);
+ }
+ } finally {
+ if (this.progressMonitor != null)
+ this.progressMonitor.done();
+ }
+ }
+ /*
+ * Makes the given working copy consistent, computes the delta and computes
+ * an AST if needed. Returns the AST.
+ */
+ public RootNode makeConsistent(RubyScript workingCopy, IProblemRequestor problemRequestor) throws RubyModelException {
+ if (!workingCopy.isConsistent()) {
+ // make working copy consistent
+ if (this.problems == null)
+ this.problems = new HashMap();
+ this.ast = workingCopy.makeConsistent(true, this.problems, this.progressMonitor);
+ this.deltaBuilder.buildDeltas();
+ // if (this.ast != null && this.deltaBuilder.delta != null)
+ // this.deltaBuilder.delta.changedAST(this.ast);
+ return this.ast;
+ }
+ if (this.ast != null)
+ return this.ast; // no need to recompute AST if known already
+ if (this.forceProblemDetection) {
+ if (RubyProject.hasRubyNature(workingCopy.getRubyProject().getProject())) {
+ HashMap problemMap;
+ if (this.problems == null) {
+ problemMap = new HashMap();
+ if (this.forceProblemDetection)
+ this.problems = problemMap;
+ } else
+ problemMap = this.problems;
+ // find problems
+ char[] contents = workingCopy.getContents();
+ RubyScriptProblemFinder.process(workingCopy, contents, problemMap, this.progressMonitor);
+ if (this.progressMonitor != null)
+ this.progressMonitor.worked(1);
+
+ } // else working copy not in a Ruby project
+ return this.ast;
+ }
+ return null;
+ }
+
+ /**
+ * Returns the working copy this operation is working on.
+ */
+ protected RubyScript getWorkingCopy() {
+ return (RubyScript) getElementToProcess();
+ }
+
+ /**
+ * @see RubyModelOperation#isReadOnly
+ */
+ public boolean isReadOnly() {
+ return true;
+ }
+
+ protected IRubyModelStatus verify() {
+ IRubyModelStatus status = super.verify();
+ if (!status.isOK()) {
+ return status;
+ }
+ RubyScript workingCopy = getWorkingCopy();
+ if (!workingCopy.isWorkingCopy()) {
+ return new RubyModelStatus(IRubyModelStatusConstants.ELEMENT_DOES_NOT_EXIST, workingCopy); // was
+ // destroyed
+ }
+ return status;
+ }
+
+ private void notifyParticipants(final RubyScript workingCopy) {
+ IRubyProject rubyProject = getWorkingCopy().getRubyProject();
+ CompilationParticipant[] participants = RubyModelManager.getRubyModelManager().compilationParticipants.getCompilationParticipants(rubyProject);
+ if (participants == null)
+ return;
+
+ final ReconcileContext context = new ReconcileContext(this, workingCopy);
+ for (int i = 0, length = participants.length; i < length; i++) {
+ final CompilationParticipant participant = participants[i];
+ SafeRunner.run(new ISafeRunnable() {
+ public void handleException(Throwable exception) {
+ if (exception instanceof Error) {
+ throw (Error) exception; // errors are not supposed
+ // to be caught
+ } else if (exception instanceof OperationCanceledException)
+ throw (OperationCanceledException) exception;
+ else if (exception instanceof UnsupportedOperationException) {
+ // might want to disable participant as it tried to
+ // modify the buffer of the working copy being
+ // reconciled
+ Util.log(exception, "Reconcile participant attempted to modify the buffer of the working copy being reconciled"); //$NON-NLS-1$
+ } else
+ Util.log(exception, "Exception occurred in reconcile participant"); //$NON-NLS-1$
+ }
+
+ public void run() throws Exception {
+ participant.reconcile(context);
+ }
+ });
+ }
+ }
+
}
\ No newline at end of file
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyElementDeltaBuilder.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyElementDeltaBuilder.java 2007-03-22 15:59:32 UTC (rev 2210)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyElementDeltaBuilder.java 2007-03-22 19:28:46 UTC (rev 2211)
@@ -60,7 +60,7 @@
/**
* Change delta
*/
- RubyElementDelta delta;
+ public RubyElementDelta delta;
/**
* List of added elements
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 2007-03-22 15:59:32 UTC (rev 2210)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyModelManager.java 2007-03-22 19:28:46 UTC (rev 2211)
@@ -45,6 +45,7 @@
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Path;
@@ -53,6 +54,7 @@
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Preferences;
import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.content.IContentTypeManager.ContentTypeChangeEvent;
import org.eclipse.core.runtime.content.IContentTypeManager.IContentTypeChangeListener;
@@ -77,6 +79,7 @@
import org.rubypeople.rdt.core.RubyCore;
import org.rubypeople.rdt.core.RubyModelException;
import org.rubypeople.rdt.core.WorkingCopyOwner;
+import org.rubypeople.rdt.core.compiler.CompilationParticipant;
import org.rubypeople.rdt.core.compiler.IProblem;
import org.rubypeople.rdt.internal.compiler.util.HashtableOfObjectToInt;
import org.rubypeople.rdt.internal.core.buffer.BufferManager;
@@ -197,7 +200,7 @@
public final IEclipsePreferences[] preferencesLookup = new IEclipsePreferences[2];
private WeakHashSet stringSymbols = new WeakHashSet(5);
- static final int PREF_INSTANCE = 0;
+ static final int PREF_INSTANCE = 0;
static final int PREF_DEFAULT = 1;
public static final IRubyScript[] NO_WORKING_COPY = new IRubyScript[0];
@@ -223,6 +226,137 @@
public static boolean PERF_VARIABLE_INITIALIZER = false;
public static boolean PERF_CONTAINER_INITIALIZER = false;
+
+ private static final Object[] NO_PARTICIPANTS = new Object[0];
+ /**
+ * Name of the extension point for contributing a compilation participant
+ */
+ public static final String COMPILATION_PARTICIPANT_EXTPOINT_ID = "compilationParticipant" ; //$NON-NLS-1$
+
+
+ public class CompilationParticipants {
+
+ private Object[] registeredParticipants = null;
+ private HashSet managedMarkerTypes;
+
+ public CompilationParticipant[] getCompilationParticipants(IRubyProject project) {
+ final Object[] participants = getRegisteredParticipants();
+ if (participants == NO_PARTICIPANTS)
+ return null;
+
+ int length = participants.length;
+ CompilationParticipant[] result = new CompilationParticipant[length];
+ int index = 0;
+ for (int i = 0; i < length; i++) {
+ if (participants[i] instanceof IConfigurationElement) {
+ final IConfigurationElement configElement = (IConfigurationElement) participants[i];
+ final int participantIndex = i;
+ SafeRunner.run(new ISafeRunnable() {
+ public void handleException(Throwable exception) {
+ Util.log(exception, "Exception occurred while creating compilation participant"); //$NON-NLS-1$
+ }
+ public void run() throws Exception {
+ Object executableExtension = configElement.createExecutableExtension("class"); //$NON-NLS-1$
+ participants[participantIndex] = executableExtension;
+ }
+ });
+ }
+ CompilationParticipant participant = (CompilationParticipant) participants[i];
+ if (participant != null && participant.isActive(project))
+ result[index++] = participant;
+ }
+ if (index == 0)
+ return null;
+ if (index < length)
+ System.arraycopy(result, 0, result = new CompilationParticipant[index], 0, index);
+ return result;
+ }
+
+ public HashSet managedMarkerTypes() {
+ if (this.managedMarkerTypes == null) {
+ // force extension points to be read
+ getRegisteredParticipants();
+ }
+ return this.managedMarkerTypes;
+ }
+
+ private synchronized Object[] getRegisteredParticipants() {
+ if (this.registeredParticipants != null) {
+ return this.registeredParticipants;
+ }
+ this.managedMarkerTypes = new HashSet();
+ IExtensionPoint extension = Platform.getExtensionRegistry().getExtensionPoint(RubyCore.PLUGIN_ID, COMPILATION_PARTICIPANT_EXTPOINT_ID);
+ if (extension == null)
+ return this.registeredParticipants = NO_PARTICIPANTS;
+ final ArrayList modifyingEnv = new ArrayList();
+ final ArrayList creatingProblems = new ArrayList();
+ final ArrayList others = new ArrayList();
+ IExtension[] extensions = extension.getExtensions();
+ // for all extensions of this point...
+ for(int i = 0; i < extensions.length; i++) {
+ IConfigurationElement[] configElements = extensions[i].getConfigurationElements();
+ // for all config elements named "compilationParticipant"
+ for(int j = 0; j < configElements.length; j++) {
+ final IConfigurationElement configElement = configElements[j];
+ String elementName =configElement.getName();
+ if (!("compilationParticipant".equals(elementName))) { //$NON-NLS-1$
+ continue;
+ }
+ // add config element in the group it belongs to
+ if ("true".equals(configElement.getAttribute("modifiesEnvironment"))) //$NON-NLS-1$ //$NON-NLS-2$
+ modifyingEnv.add(configElement);
+ else if ("true".equals(configElement.getAttribute("createsProblems"))) //$NON-NLS-1$ //$NON-NLS-2$
+ creatingProblems.add(configElement);
+ else
+ others.add(configElement);
+ // add managed marker types
+ IConfigurationElement[] managedMarkers = configElement.getChildren("managedMarker"); //$NON-NLS-1$
+ for (int k = 0, length = managedMarkers.length; k < length; k++) {
+ IConfigurationElement element = managedMarkers[k];
+ String markerType = element.getAttribute("markerType"); //$NON-NLS-1$
+ if (markerType != null)
+ this.managedMarkerTypes.add(markerType);
+ }
+ }
+ }
+ int size = modifyingEnv.size() + creatingProblems.size() + others.size();
+ if (size == 0)
+ return this.registeredParticipants = NO_PARTICIPANTS;
+
+ // sort config elements in each group
+ IConfigurationElement[] configElements = new IConfigurationElement[size];
+ int index = 0;
+ index = sortParticipants(modifyingEnv, configElements, index);
+ index = sortParticipants(creatingProblems, configElements, index);
+ index = sortParticipants(others, configElements, index);
+ return this.registeredParticipants = configElements;
+ }
+
+ private int sortParticipants(ArrayList group, IConfigurationElement[] configElements, int index) {
+ int size = group.size();
+ if (size == 0) return index;
+ Object[] elements = group.toArray();
+ Util.sort(elements, new Util.Comparer() {
+ public int compare(Object a, Object b) {
+ if (a == b) return 0;
+ String id = ((IConfigurationElement) a).getAttribute("id"); //$NON-NLS-1$
+ if (id == null) return -1;
+ IConfigurationElement[] requiredElements = ((IConfigurationElement) b).getChildren("requires"); //$NON-NLS-1$
+ for (int i = 0, length = requiredElements.length; i < length; i++) {
+ IConfigurationElement required = requiredElements[i];
+ if (id.equals(required.getAttribute("id"))) //$NON-NLS-1$
+ return 1;
+ }
+ return -1;
+ }
+ });
+ for (int i = 0; i < size; i++)
+ configElements[index+i] = (IConfigurationElement) elements[i];
+ return index + size;
+ }
+ }
+
+ public final CompilationParticipants compilationParticipants = new CompilationParticipants();
/**
* Update the classpath variable cache
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyScript.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyScript.java 2007-03-22 15:59:32 UTC (rev 2210)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyScript.java 2007-03-22 19:28:46 UTC (rev 2211)
@@ -26,6 +26,8 @@
import java.io.CharArrayReader;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
import org.eclipse.core.resources.IContainer;
@@ -36,6 +38,7 @@
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.jruby.ast.Node;
+import org.jruby.ast.RootNode;
import org.jruby.lexer.yacc.SyntaxException;
import org.rubypeople.rdt.core.CompletionRequestor;
import org.rubypeople.rdt.core.IBuffer;
@@ -55,6 +58,7 @@
import org.rubypeople.rdt.core.RubyCore;
import org.rubypeople.rdt.core.RubyModelException;
import org.rubypeople.rdt.core.WorkingCopyOwner;
+import org.rubypeople.rdt.core.compiler.CategorizedProblem;
import org.rubypeople.rdt.internal.codeassist.CompletionEngine;
import org.rubypeople.rdt.internal.core.buffer.BufferManager;
import org.rubypeople.rdt.internal.core.parser.RubyParser;
@@ -112,13 +116,25 @@
RubyModelManager.PerWorkingCopyInfo perWorkingCopyInfo = getPerWorkingCopyInfo();
IRubyProject project = getRubyProject();
+
+ boolean createAST;
+ HashMap problems;
+ if (info instanceof ASTHolderCUInfo) {
+ ASTHolderCUInfo astHolder = (ASTHolderCUInfo) info;
+ createAST = true;
+ problems = astHolder.problems;
+ } else {
+ createAST = false;
+ problems = null;
+ }
boolean computeProblems = RubyProject.hasRubyNature(project.getProject()) && perWorkingCopyInfo != null && perWorkingCopyInfo.isActive();
+ Node ast = null;
try {
RubyParser parser = new RubyParser();
- Node node = parser.parse((IFile) getResource(), new CharArrayReader(contents));
+ ast = parser.parse((IFile) getResource(), new CharArrayReader(contents));
RubyScriptStructureBuilder visitor = new RubyScriptStructureBuilder(this, unitInfo, newElements);
- if (node != null) node.accept(visitor);
+ if (ast != null) ast.accept(visitor);
unitInfo.setIsStructureKnown(true);
} catch (SyntaxException e) {
unitInfo.setIsStructureKnown(false);
@@ -137,9 +153,32 @@
// compute other problems if needed
if (computeProblems) {
perWorkingCopyInfo.beginReporting();
- RubyScriptProblemFinder.process(this, contents, perWorkingCopyInfo, pm);
+ RubyScriptProblemFinder.process(this, contents, problems, pm);
+ if (problems == null) {
+ // report problems to the problem requestor
+ problems = new HashMap();
+ RubyScriptProblemFinder.process(this, contents, problems, pm);
+ try {
+ perWorkingCopyInfo.beginReporting();
+ for (Iterator iteraror = problems.values().iterator(); iteraror.hasNext();) {
+ CategorizedProblem[] categorizedProblems = (CategorizedProblem[]) iteraror.next();
+ if (categorizedProblems == null) continue;
+ for (int i = 0, length = categorizedProblems.length; i < length; i++) {
+ perWorkingCopyInfo.acceptProblem(categorizedProblems[i]);
+ }
+ }
+ } finally {
+ perWorkingCopyInfo.endReporting();
+ }
+ } else {
+ // collect problems
+ RubyScriptProblemFinder.process(this, contents, problems, pm);
+ }
perWorkingCopyInfo.endReporting();
}
+ if (createAST) {
+ ((ASTHolderCUInfo) info).ast = (RootNode) ast;
+ }
return unitInfo.isStructureKnown();
}
@@ -529,25 +568,22 @@
* @see IOpenable#makeConsistent(IProgressMonitor)
*/
public void makeConsistent(IProgressMonitor monitor) throws RubyModelException {
- makeConsistent(false, monitor);
+ makeConsistent(false, null, monitor);
}
- public RubyScript makeConsistent(boolean createAST, IProgressMonitor monitor) throws RubyModelException {
+ public RootNode makeConsistent(boolean createAST, HashMap problems, IProgressMonitor monitor) throws RubyModelException {
if (isConsistent()) return null;
- // create a new info and make it the current info
- // (this will remove the info and its children just before storing the new infos)
- // TODO When createAST is specified, actually do it!
-// if (createAST) {
-// ASTHolderCUInfo info = new ASTHolderCUInfo();
-// openWhenClosed(info, monitor);
-// RubyScript result = info.ast;
-// info.ast = null;
-// return result;
-// } else {
- openWhenClosed(createElementInfo(), monitor);
- return null;
-// }
+ if (createAST) {
+ ASTHolderCUInfo info = new ASTHolderCUInfo();
+ info.problems = problems;
+ openWhenClosed(info, monitor);
+ RootNode result = info.ast;
+ info.ast = null;
+ return result;
+ }
+ openWhenClosed(createElementInfo(), monitor);
+ return null;
}
/*
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyScriptProblemFinder.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyScriptProblemFinder.java 2007-03-22 15:59:32 UTC (rev 2210)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyScriptProblemFinder.java 2007-03-22 19:28:46 UTC (rev 2211)
@@ -4,8 +4,7 @@
package org.rubypeople.rdt.internal.core;
import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.Iterator;
+import java.util.HashMap;
import java.util.List;
import org.eclipse.core.resources.IFile;
@@ -14,8 +13,8 @@
import org.jruby.ast.visitor.NodeVisitor;
import org.jruby.lexer.yacc.SyntaxException;
import org.rubypeople.rdt.core.IProblemRequestor;
+import org.rubypeople.rdt.core.IRubyModelMarker;
import org.rubypeople.rdt.core.RubyModelException;
-import org.rubypeople.rdt.core.compiler.IProblem;
import org.rubypeople.rdt.internal.core.parser.RdtWarnings;
import org.rubypeople.rdt.internal.core.parser.RubyParser;
import org.rubypeople.rdt.internal.core.parser.TaskParser;
@@ -29,24 +28,18 @@
public class RubyScriptProblemFinder {
// DSC convert to ImmediateWarnings
- public static void process(RubyScript script, char[] charContents,
- IProblemRequestor problemRequestor, IProgressMonitor pm) {
+ public static void process(RubyScript script, char[] charContents, HashMap problems, IProgressMonitor pm) {
RdtWarnings warnings = new RdtWarnings();
RubyParser parser = new RubyParser(warnings);
String contents = new String(charContents);
- runLint(script, problemRequestor, parser, contents);
+// runLint(script, problemRequestor, parser, contents); FIXME Make all these compilationParticipants
TaskParser taskParser = new TaskParser(script.getRubyProject().getOptions(true));
taskParser.parse(contents);
- List problems = new ArrayList();
- problems.addAll(warnings.getWarnings());
- problems.addAll(taskParser.getTasks());
- for (Iterator iter = problems.iterator(); iter.hasNext();) {
- IProblem problem = (IProblem) iter.next();
- problemRequestor.acceptProblem(problem);
- }
+ problems.put(IRubyModelMarker.RUBY_MODEL_PROBLEM_MARKER, warnings.getWarnings());
+ problems.put(IRubyModelMarker.TASK_MARKER, taskParser.getTasks());
}
private static void runLint(RubyScript script, IProblemRequestor problemRequestor, RubyParser parser, String contents) {
@@ -64,5 +57,4 @@
e.printStackTrace();
}
}
-
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|