| <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> |
| <html> |
| <head> |
| <title>Refactoring Proposal</title> |
| <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> |
| <link rel="stylesheet" href="http://dev.eclipse.org/default_style.css" |
| type="text/css" /> |
| </head> |
| <body style="background-color: rgb(255, 255, 255); color: rgb(0, 0, 0)"> |
| |
| |
| <h2 align="center">Participating in Refactorings</h2> |
| |
| |
| <p>The document describes the refactoring processor/participant. Some of |
| the features outlined in the original proposal didn't get implemented |
| for 3.0. Those features are marked in <font color="#0000ff">blue</font>.</p> |
| <h3>Overall architecture</h3> |
| <p>To enable other plug-ins to participate in certain refactorings their |
| implementations are split into a processor/participant architecture. An |
| extensible refactoring has one processor and 0 .. n participants. The |
| major characteristics of processors and participants are:</p> |
| |
| <ul> |
| <li><b><i>Processor</i></b>: the main actor of a refactoring. It can |
| exist without any participants. A processor is almost identical to a |
| current refactoring and is determined based on the element to be |
| refactored.</li> |
| <li><b><i>Participant</i></b>: participates in a refactoring. A |
| participant can't exist without a processor and is determined based on |
| the element to be refactored. A participant can participate in |
| precondition checking and change creation. There is no support for |
| participants to provide additional UI. A typical participant, provided |
| by a JSP/Web plug-in, is a rename method participant. The participant |
| updates references to the method to be renamed in JSP files. This |
| includes both updating references in embedded Java code as well as |
| updating references in tags (e.g. usebean tag, etc.). |
| <p>Participants can only participate in changes triggered by a |
| processor. There is no support to participate in changes caused by |
| another participant. The processor/participant architecture will not |
| support arbitrary refactoring composition. Refactoring composition |
| requires that a precondition check describes its result in some kind of |
| postcondition which is then used as input for precondition checking of |
| the next sibling refactoring in the composite. Consider the following |
| example: a composite refactoring consists of the two refactorings (a) |
| create class A.java and (b) add a method to A.java. One precondition |
| check of refactoring (b) is: does A.java exist? Therefore evaluating |
| the preconditions of both refactorings in isolation will fail. In |
| contrast precondition checking in the processor/participant |
| architecture assumes that the changes performed by the processor and |
| the participants are "unrelated" to each other and therefore |
| the preconditions can be checked in isolation.</p> |
| </li> |
| </ul> |
| <p>Both processors and participants are contributed via extension |
| points. XML expressions (comparable to those used for contributing to a |
| pop-up menu) are used to describe their availability. <font |
| color="#0000ff">For 3.0 only participants can be contributed via XML. |
| Processors are still contributed via code. We had a prototype running where |
| processors where contributed as well. The concerns were: performance, |
| since on selection change we evaluated extension points, the fact that |
| there can be more than one processor that handles an element (user |
| arbitration), and problems to pick processors when multiple elements |
| were selected</font>.</p> |
| <p>For each refactoring that is split into a processor/participant there |
| is a "generic" action. This action creates a generic |
| refactoring responsible to find the right processor and participants |
| depending on the element(s) selected. Having such a generic action |
| ensures that the same refactoring is triggered independent from where |
| the refactoring is activated. The UI for the refactoring is determined |
| by the processor chosen to process the element. Since processors and |
| participants create errors and change objects typically not known by the |
| refactoring framework the error and preview page must be extensible as |
| well. The concrete status error viewer is picked depending on the type |
| of status returned from precondition checking. The type of the change |
| objects determines the concrete preview viewer. Additionally special |
| content providers are needed for different change types to ensure proper |
| rendering the of tree presenting all changes. <font color="#0000ff">Since |
| processor are specific and contributed via code the actions aren't |
| generic either. They still refer to concrete processors. Additionally |
| the actions determines the user interface to use. Pluggable status and |
| preview viewers are implemented, however the tree showing the modified |
| resources is still JDT specific (no pluggable content provider; the |
| implementation of such a tree has very much in common with a generic |
| Navigator).</font></p> |
| <p>The picture below presents an overview of the new |
| processor/participant split. Classes labeled as "generic" are |
| provided by the refactoring framework. Classes labeled as |
| "concrete" are concrete implementations provided by clients. A |
| dashed line expresses a references between classes configurable via |
| extension points. Solid lines indicate a fixed reference not |
| configurable by client plug-ins.</p> |
| <p align="center"><img border="0" src="Diagramm.gif"></p> |
| <p>The details of the processor/participant architecture are illustrated |
| by the examples below. </p> |
| <h3>Renaming a Java method</h3> |
| <p>Renaming method A#foo() into A#bar() takes place according to the |
| following steps:</p> |
| <ol> |
| <li> |
| <p>The generic rename refactoring searches in Eclipse's registry for a |
| processor that is able to "rename" a Java method. An example |
| of a processor capable to rename a Java method is:</p> |
| <pre><renameProcessor |
| id="org.eclipse.jdt.renameProcessor.Method" |
| class="org.eclipse.jdt.internal.corext.refactoring.rename.RenameMethodProcessor"> |
| <enablement> |
| <with variable="element"/> |
| <instanceof value="org.eclipse.jdt.core.IMethod"/> |
| </with> |
| </enablement> |
| </renameProcessor></pre> |
| <p>Due to its extensibility the new generic rename refactoring can only |
| assume that it is renaming an element of type object. Hence a Java |
| method rename processor must constrain its availability to IMethods. |
| The <enablement> element can be used to describe a processor's |
| availability depending on the state of the element to be renamed. <font |
| color="#0000ff">This isn't implemented since processors aren't |
| pluggable</font>.</p> |
| <p>The rename action is disabled if no processor can be found.</p> |
| </li> |
| <li> |
| <p>Find all interested participants of a Java method rename. A typical |
| rename Java method participant looks as follows:</p> |
| <pre><renameParticipant |
| class="org.example.jsp.refactoring.RenameMethodParticipant" |
| id="org.example.jsp.refactoring.renameMethodParticipant"> |
| <enablement> |
| <with variable="affectedNatures"> |
| <iterate operator="or"> |
| <equals value="org.eclipse.jdt.core.jspnature"/> |
| </iterate> |
| </with> |
| <with varaible="element"> |
| <instanceof value="org.eclipse.jdt.core.IMethod"/> |
| <property name="org.eclipse.jsp.core.internal.isPrivate" value="false"/> |
| </with> |
| </enablement> |
| </renameParticipant> </pre> |
| <p>Since participants are typically defined in higher level pug-ins |
| care must be taken to avoid unnecessary plug-in loading. This is |
| achieved by a two step approach: |
| <ol> |
| <li>the refactoring processor is responsible to compute a set of |
| affected projects, called the project scope of the refactoring. E.g. |
| when renaming a Java method these are all projects which directly or |
| indirectly reference the project in which the method to be renamed is |
| declared. Refactoring processors for other languages might use a |
| different algorithm to compute the set of affected projects. A |
| refactoring participant must handle the affectedNatures variable. In |
| the example above this means the JSP rename method participant gets |
| only loaded iff one of the affected projects has a JSP nature. This |
| ensures that the JSP participant gets only loaded if the user does JSP |
| development.</li> |
| <li>Besides the project scope a refactoring participant has to handle |
| the variable element to express for which objects it is applicable. In |
| the example above the rename method participant in the JSP world is |
| applicable to IMethods. Furthermore it requires that the method isn't |
| private (since private methods can't be referenced outside its |
| declaring type renaming them can't affect any JSP files).<br> |
| </li> |
| </ol> |
| |
| </li> |
| <li> |
| <p>Compute the activation status of the rename refactoring by |
| considering activation status of the processor and all participants. If |
| the returned status doesn't contain any fatal error, determine the UI |
| depending on the state of the processor. Processors are UI independent |
| hence they can't have any references to the UI. So the processor UI |
| relationship must be defined somewhere else. <font color="#0000ff">In |
| the current implementation this is done by the action. If we want to |
| have "generic" actions then the user interface must be |
| contributed via an extension point as well.</font></p> |
| |
| |
| |
| </li> |
| <li>When the user presses the OK or Preview button the processor |
| forwards the collected input (e.g new name, etc.) to all participants. |
| Next user input checking is performed considering the rename processor |
| and all loaded participants. Depending on the outcome of this check, |
| the refactoring is executed by computing a composite change containing |
| the changes from the processor and the participants.</li> |
| <li>Picking the right error and preview viewer happens analogous to |
| other viewer selection in Eclipse.</li> |
| </ol> |
| <h3>Derived Elements</h3> |
| <p>The concept of derived elements is discussed using the rename Java |
| field refactoring. Fields are commonly used as properties with |
| corresponding setter and getter methods. A special rename field |
| refactoring is aware of this relationship and renames the setter and |
| getter method when the field gets renamed. Participants are contributed |
| based on the element to be changed. Since the main element is a field no |
| rename method participants are loaded. Therefore a processor can provide |
| a set of additional elements that it manipulates. These elements are |
| called derived elements. In contrast to participants that work on the |
| main element (in our example the Java field), derived participants may |
| depend on user input. E.g. in JDT renaming the corresponding setter and |
| getter of a field can be controlled by the user. So derived participants |
| can only be loaded after the user has pressed the finish or preview |
| button. Since they missed the initial condition checking phase of the |
| precondition checking the result of the initial condition check of |
| derived participants is merged into the result of the processor's final |
| condition checking.</p> |
| |
| <h3>Resource Modifications</h3> |
| <p>The concept of resource modifications is discussed using the rename |
| Java package refactoring. Although packages are mapped to folders on the |
| file system, renaming a package is a different operation than renaming |
| its underlying folder. Renaming a package maps to the following resource |
| operations:</p> |
| <ol> |
| <li>create a folder with the new name, if such a folder doesn't exist |
| yet.</li> |
| <li>move all files from the underlying folder to the new folder created |
| in step 1.</li> |
| <li>delete the underlying folder if it is empty.</li> |
| </ol> |
| <p>Now consider the following example: package p1 contains Java and HTML |
| files. Additionally, there is a HTML plug-in that wants to participate |
| in moving HTML files to update href elements in other HTML files. Since |
| a HTML plug-in doesn't know anything about Java it can't participate in |
| renaming a package. Even if it had a reference to the Java plug-in, |
| participating in a package rename to determine this situation would |
| require that the HTML plug-in doubles the logic for mapping a package |
| rename to the underlying file system. To solve this problem a |
| refactoring processor must express higher level operations in terms of |
| resource modifications when possible. These resource modifications are |
| then taken to determine the appropriate set of participants. This means |
| for the HTML plug-in that it is enough to register a file move |
| participant. Like participants for derived elements participants for |
| resource modifications are computed after user input. Hence their |
| initial condition checking is merged into the processor's final |
| condition checking as well.</p> |
| <h3>Specializing Processors</h3> |
| <p>Source folders (package fragment roots) are a special kind of |
| folders. Besides their underlying folder on disk additional information |
| about the build path and the output folder is associated with a source |
| folder. Will there be a special rename source folder processor or a |
| folder rename participant? The Java model offers a method to rename a |
| source folder which updates the necessary build path information. |
| Therefore implementing source folder rename with a participant |
| duplicates that code in a participant. Furthermore the provided delta |
| looks different. A participant implementation triggers the following |
| deltas:</p> |
| <ul> |
| <li>a resource delta notifying about the folder rename.</li> |
| <li>a Java model delta notifying about the build path changes.</li> |
| <li>a Java model delta notifying about the source folder rename without |
| build path updates. This delta occurs since the Java model listens to |
| resource deltas and tries to map them back to Java model deltas.</li> |
| </ul> |
| <p>A special source folder processor triggers the following deltas:</p> |
| <ul> |
| <li>a Java model delta notifying about the source folder rename and the |
| build path update.</li> |
| <li>a resource delete notifying the folder rename.</li> |
| </ul> |
| <p>Clearly the second delta is more appropriate. It is exactly what |
| clients listening to deltas expect. Therefore the source folder rename |
| is implemented using a special processor. <font color="#0000ff">In the |
| current implementation this is hard wired since processors can't be |
| contributed. If we support contributing processors the two processors |
| have to be defined as follows:</font></p> |
| <blockquote><pre> |
| #### The processor to rename resource (files, folders, projects, ....) |
| <renameProcessor |
| id="org.eclipse.jdt.renameProcessor.IResource" |
| class="org.eclipse.jdt.internal.corext.refactoring.rename.RenameResourceProcessor"> |
| <enablement> |
| <instanceof value="org.eclipse.core.resources.IResource"/> |
| </enablement> |
| </renameProcessor> |
| |
| #### The processor to rename source folders |
| <renameProcessor |
| id="org.eclipse.jdt.renameProcessor.IPackageFragmentRoot" |
| >> override="org.eclipse.jdt.renameProcessor.IResource" |
| class="org.eclipse.jdt.internal.corext.refactoring.rename.RenameSourceFolderProcessor"> |
| <enablement> |
| <or> |
| <instanceof value="org.eclipse.jdt.core.IPackageFragmentRoot"/> |
| <and> |
| <instanceof value="org.eclipse.core.resources.IFolder"/> |
| <property name="org.eclipse.jdt.core.isSourceFolder" value="true"/> |
| </and> |
| </or> |
| </enablement> |
| </renameProcessor></pre></blockquote> |
| <p>With the override attribute (marked with ">>"" |
| in the above example) the rename package fragment root processor |
| specifies that it replaces the rename resource processor if its object |
| state matches. This is the case if the element to be renamed is of |
| instance IPackageFragmenRoot (which is the case if a source folder is |
| selected in the package explorer) or if the element is of instance |
| IFolder and it can be converted into a source folder (which is the case |
| if a source folder is selected in the resource navigator).</p> |
| <p>What happens if the processor isn't unique for an element? In this |
| case the user has to decide which processor he wants to use to perform |
| the refactoring. Analogous to the Open With menu item the user is able |
| to define a preferred processor to avoid unnecessary user arbitration.</p> |
| |
| |
| |
| <h3>Sharable participants</h3> |
| <p>When refactoring more than one element, for example moving two HTML |
| files from one folder to another, there could be one participant |
| instance handling both file or two participant instances each handling |
| one file. Since it can't be defined up-front what the best solution is, a |
| participant can be tagged as sharable. If it is tagged so, it will be |
| reused for all elements which match the same extension as the sharable |
| participant.</p> |
| <h3>Sharable Precondition checking</h3> |
| <p>Preconditions like validateEdit must be check together for the processor and all participants (otherwise the user would see multiple dialogs). Participants can therefore contribute to shared precondition checkers instead of implementing the check by themselves.</p> |
| </body> |
| </html> |