28f09006b8ccc5b5a95c054504abc9193d2dac3d
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / CreateElementInCUOperation.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.core;
12
13 import net.sourceforge.phpdt.core.IBuffer;
14 import net.sourceforge.phpdt.core.ICompilationUnit;
15 import net.sourceforge.phpdt.core.IJavaElement;
16 import net.sourceforge.phpdt.core.IJavaModelStatus;
17 import net.sourceforge.phpdt.core.IJavaModelStatusConstants;
18 import net.sourceforge.phpdt.core.JavaModelException;
19 import net.sourceforge.phpdt.core.jdom.DOMFactory;
20 import net.sourceforge.phpdt.core.jdom.IDOMCompilationUnit;
21 import net.sourceforge.phpdt.core.jdom.IDOMNode;
22 import net.sourceforge.phpdt.internal.core.jdom.DOMNode;
23 import net.sourceforge.phpdt.internal.core.util.Util;
24
25 import org.eclipse.core.resources.IResource;
26 import org.eclipse.core.resources.IWorkspace;
27 import org.eclipse.core.runtime.jobs.ISchedulingRule;
28
29 /**
30  * <p>This abstract class implements behavior common to <code>CreateElementInCUOperations</code>.
31  * To create a compilation unit, or an element contained in a compilation unit, the
32  * source code for the entire compilation unit is updated and saved.
33  *
34  * <p>The element being created can be positioned relative to an existing
35  * element in the compilation unit via the methods <code>#createAfter</code>
36  * and <code>#createBefore</code>. By default, the new element is positioned
37  * as the last child of its parent element.
38  *
39  */
40 public abstract class CreateElementInCUOperation extends JavaModelOperation {
41         /**
42          * The compilation unit DOM used for this operation
43          */
44         protected IDOMCompilationUnit fCUDOM;
45         /**
46          * A constant meaning to position the new element
47          * as the last child of its parent element.
48          */
49         protected static final int INSERT_LAST = 1;
50         /**
51          * A constant meaning to position the new element
52          * after the element defined by <code>fAnchorElement</code>.
53          */
54         protected static final int INSERT_AFTER = 2;
55
56         /**
57          * A constant meaning to position the new element
58          * before the element defined by <code>fAnchorElement</code>.
59          */
60         protected static final int INSERT_BEFORE = 3;
61         /**
62          * One of the position constants, describing where
63          * to position the newly created element.
64          */
65         protected int fInsertionPolicy = INSERT_LAST;
66         /**
67          * The element that the newly created element is
68          * positioned relative to, as described by
69          * <code>fInsertPosition</code>, or <code>null</code>
70          * if the newly created element will be positioned
71          * last.
72          */
73         protected IJavaElement fAnchorElement = null;
74         /**
75          * A flag indicating whether creation of a new element occurred.
76          * A request for creating a duplicate element would request in this
77          * flag being set to <code>false</code>. Ensures that no deltas are generated
78          * when creation does not occur.
79          */
80         protected boolean fCreationOccurred = true;
81         /**
82          * The element that is being created.
83          */
84         protected DOMNode fCreatedElement;
85         /**
86          * The position of the element that is being created.
87          */
88         protected int fInsertionPosition = -1;
89         /**
90          * The number of characters the new element replaces,
91          * or 0 if the new element is inserted,
92          * or -1 if the new element is append to the end of the CU.
93          */
94         protected int fReplacementLength = -1;
95         /**
96          * Constructs an operation that creates a Java Language Element with
97          * the specified parent, contained within a compilation unit.
98          */
99         public CreateElementInCUOperation(IJavaElement parentElement) {
100                 super(null, new IJavaElement[]{parentElement});
101                 initializeDefaultPosition();
102         }
103         /**
104          * Only allow cancelling if this operation is not nested.
105          */
106         protected void checkCanceled() {
107                 if (!isNested) {
108                         super.checkCanceled();
109                 }
110         }
111         /**
112          * Instructs this operation to position the new element after
113          * the given sibling, or to add the new element as the last child
114          * of its parent if <code>null</code>.
115          */
116         public void createAfter(IJavaElement sibling) {
117                 setRelativePosition(sibling, INSERT_AFTER);
118         }
119         /**
120          * Instructs this operation to position the new element before
121          * the given sibling, or to add the new element as the last child
122          * of its parent if <code>null</code>.
123          */
124         public void createBefore(IJavaElement sibling) {
125                 setRelativePosition(sibling, INSERT_BEFORE);
126         }
127         /**
128          * Execute the operation - generate new source for the compilation unit
129          * and save the results.
130          *
131          * @exception JavaModelException if the operation is unable to complete
132          */
133         protected void executeOperation() throws JavaModelException {
134                 try {
135                         beginTask(getMainTaskName(), getMainAmountOfWork());
136                         JavaElementDelta delta = newJavaElementDelta();
137                         ICompilationUnit unit = getCompilationUnit();
138                         generateNewCompilationUnitDOM(unit);
139                         if (fCreationOccurred) {
140                                 //a change has really occurred
141                                 IBuffer buffer = unit.getBuffer();
142                                 if (buffer  == null) return;
143                                 char[] bufferContents = buffer.getCharacters();
144                                 if (bufferContents == null) return;
145                                 char[] elementContents = net.sourceforge.phpdt.internal.core.util.Util.normalizeCRs(fCreatedElement.getCharacters(), bufferContents);
146                                 switch (fReplacementLength) {
147                                         case -1 : 
148                                                 // element is append at the end
149                                                 buffer.append(elementContents);
150                                                 break;
151                                         case 0 :
152                                                 // element is inserted
153                                                 buffer.replace(fInsertionPosition, 0, elementContents);
154                                                 break;
155                                         default :
156                                                 // element is replacing the previous one
157                                                 buffer.replace(fInsertionPosition, fReplacementLength, elementContents);
158                                 }
159                                 unit.save(null, false);
160                                 boolean isWorkingCopy = unit.isWorkingCopy();
161                                 if (!isWorkingCopy)
162                                         this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
163                                 worked(1);
164                                 resultElements = generateResultHandles();
165                                 if (!isWorkingCopy // if unit is working copy, then save will have already fired the delta
166                                                 && !Util.isExcluded(unit)
167                                                 && unit.getParent().exists()) {
168                                         for (int i = 0; i < resultElements.length; i++) {
169                                                 delta.added(resultElements[i]);
170                                         }
171                                         addDelta(delta);
172                                 } // else unit is created outside classpath
173                                   // non-java resource delta will be notified by delta processor
174                         }
175                 } finally {
176                         done();
177                 }
178         }
179         /**
180          * Returns a JDOM document fragment for the element being created.
181          */
182         protected abstract IDOMNode generateElementDOM() throws JavaModelException;
183         /**
184          * Returns the DOM with the new source to use for the given compilation unit.
185          */
186         protected void generateNewCompilationUnitDOM(ICompilationUnit cu) throws JavaModelException {
187                 IBuffer buffer = cu.getBuffer();
188                 if (buffer == null) return;
189                 char[] prevSource = buffer.getCharacters();
190                 if (prevSource == null) return;
191         
192                 // create a JDOM for the compilation unit
193                 fCUDOM = (new DOMFactory()).createCompilationUnit(prevSource, cu.getElementName());
194                 IDOMNode child = generateElementDOM();
195                 if (child != null) {
196                         insertDOMNode(fCUDOM, child);
197                 }
198                 worked(1);
199         }
200         /**
201          * Creates and returns the handle for the element this operation created.
202          */
203         protected abstract IJavaElement generateResultHandle();
204         /**
205          * Creates and returns the handles for the elements this operation created.
206          */
207         protected IJavaElement[] generateResultHandles() throws JavaModelException {
208                 return new IJavaElement[]{generateResultHandle()};
209         }
210         /**
211          * Returns the compilation unit in which the new element is being created.
212          */
213         protected ICompilationUnit getCompilationUnit() {
214                 return getCompilationUnitFor(getParentElement());
215         }
216         /**
217          * Returns the amount of work for the main task of this operation for
218          * progress reporting.
219          */
220         protected int getMainAmountOfWork(){
221                 return 2;
222         }
223         /**
224          * Returns the name of the main task of this operation for
225          * progress reporting.
226          */
227         public abstract String getMainTaskName();
228         
229         protected ISchedulingRule getSchedulingRule() {
230                 IResource resource = getCompilationUnit().getResource();
231                 IWorkspace workspace = resource.getWorkspace();
232                 return workspace.getRuleFactory().modifyRule(resource);
233         }
234         /**
235          * Returns the elements created by this operation.
236          */
237         public IJavaElement[] getResultElements() {
238                 return resultElements;
239         }
240         /**
241          * Sets the default position in which to create the new type
242          * member. By default, the new element is positioned as the
243          * last child of the parent element in which it is created.
244          * Operations that require a different default position must
245          * override this method.
246          */
247         protected void initializeDefaultPosition() {
248         
249         }
250         /**
251          * Inserts the given child into the given JDOM, 
252          * based on the position settings of this operation.
253          *
254          * @see createAfter(IJavaElement)
255          * @see createBefore(IJavaElement);
256          */
257         protected void insertDOMNode(IDOMNode parent, IDOMNode child) {
258                 if (fInsertionPolicy != INSERT_LAST) {
259                         IDOMNode sibling = ((JavaElement)fAnchorElement).findNode(fCUDOM);
260                         if (sibling != null && fInsertionPolicy == INSERT_AFTER) {
261                                 sibling = sibling.getNextNode();
262                         }
263                         if (sibling != null) {
264                                 sibling.insertSibling(child);
265                                 fCreatedElement = (DOMNode)child;
266                                 fInsertionPosition = ((DOMNode)sibling).getStartPosition();
267                                 fReplacementLength = 0;
268                                 return;
269                         }
270                 }
271                 //add as the last element of the parent
272                 parent.addChild(child);
273                 fCreatedElement = (DOMNode)child;
274                 fInsertionPosition = ((DOMNode)parent).getInsertionPosition();
275         //      fInsertionPosition = lastChild == null ? ((DOMNode)parent).getInsertionPosition() : lastChild.getInsertionPosition();
276                 fReplacementLength = parent.getParent() == null ? -1 : 0;
277         }
278         /**
279          * Sets the name of the <code>DOMNode</code> that will be used to
280          * create this new element.
281          * Used by the <code>CopyElementsOperation</code> for renaming.
282          * Only used for <code>CreateTypeMemberOperation</code>
283          */
284         protected void setAlteredName(String newName) {
285         }
286         /**
287          * Instructs this operation to position the new element relative
288          * to the given sibling, or to add the new element as the last child
289          * of its parent if <code>null</code>. The <code>position</code>
290          * must be one of the position constants.
291          */
292         protected void setRelativePosition(IJavaElement sibling, int policy) throws IllegalArgumentException {
293                 if (sibling == null) {
294                         fAnchorElement = null;
295                         fInsertionPolicy = INSERT_LAST;
296                 } else {
297                         fAnchorElement = sibling;
298                         fInsertionPolicy = policy;
299                 }
300         }
301         /**
302          * Possible failures: <ul>
303          *  <li>NO_ELEMENTS_TO_PROCESS - the compilation unit supplied to the operation is
304          *              <code>null</code>.
305          *  <li>INVALID_NAME - no name, a name was null or not a valid
306          *              import declaration name.
307          *  <li>INVALID_SIBLING - the sibling provided for positioning is not valid.
308          * </ul>
309          * @see IJavaModelStatus
310          * @see JavaConventions
311          */
312         public IJavaModelStatus verify() {
313                 if (getParentElement() == null) {
314                         return new JavaModelStatus(IJavaModelStatusConstants.NO_ELEMENTS_TO_PROCESS);
315                 }
316                 if (fAnchorElement != null) {
317                         IJavaElement domPresentParent = fAnchorElement.getParent();
318                         if (domPresentParent.getElementType() == IJavaElement.IMPORT_CONTAINER) {
319                                 domPresentParent = domPresentParent.getParent();
320                         }
321                         if (!domPresentParent.equals(getParentElement())) {
322                                 return new JavaModelStatus(IJavaModelStatusConstants.INVALID_SIBLING, fAnchorElement);
323                         }
324                 }
325                 return JavaModelStatus.VERIFIED_OK;
326         }
327 }