KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > pde > internal > ui > editor > context > XMLInputContext


1 /*******************************************************************************
2  * Copyright (c) 2003, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.pde.internal.ui.editor.context;
12 import java.util.ArrayList JavaDoc;
13 import java.util.HashMap JavaDoc;
14 import java.util.Iterator JavaDoc;
15
16 import org.eclipse.core.filebuffers.IDocumentSetupParticipant;
17 import org.eclipse.jface.text.BadLocationException;
18 import org.eclipse.jface.text.IDocument;
19 import org.eclipse.jface.text.IRegion;
20 import org.eclipse.jface.text.Region;
21 import org.eclipse.jface.text.TextUtilities;
22 import org.eclipse.pde.core.IModelChangedEvent;
23 import org.eclipse.pde.internal.core.text.IDocumentAttribute;
24 import org.eclipse.pde.internal.core.text.IDocumentNode;
25 import org.eclipse.pde.internal.core.text.IDocumentTextNode;
26 import org.eclipse.pde.internal.core.util.PDEXMLHelper;
27 import org.eclipse.pde.internal.ui.editor.PDEFormEditor;
28 import org.eclipse.text.edits.DeleteEdit;
29 import org.eclipse.text.edits.InsertEdit;
30 import org.eclipse.text.edits.MoveSourceEdit;
31 import org.eclipse.text.edits.MoveTargetEdit;
32 import org.eclipse.text.edits.ReplaceEdit;
33 import org.eclipse.text.edits.TextEdit;
34 import org.eclipse.ui.IEditorInput;
35
36 public abstract class XMLInputContext extends UTF8InputContext {
37     protected HashMap JavaDoc fOperationTable = new HashMap JavaDoc();
38     protected HashMap JavaDoc fMoveOperations = new HashMap JavaDoc();
39
40     /**
41      * @param editor
42      * @param input
43      */

44     public XMLInputContext(PDEFormEditor editor, IEditorInput input, boolean primary) {
45         super(editor, input, primary);
46     }
47
48     protected IDocumentSetupParticipant getDocumentSetupParticipant() {
49         return new XMLDocumentSetupParticpant();
50     }
51     
52     /* (non-Javadoc)
53      * @see org.eclipse.pde.internal.ui.neweditor.context.InputContext#addTextEditOperation(java.util.ArrayList, org.eclipse.pde.core.IModelChangedEvent)
54      */

55     protected void addTextEditOperation(ArrayList JavaDoc ops, IModelChangedEvent event) {
56         Object JavaDoc[] objects = event.getChangedObjects();
57         if (objects != null) {
58             for (int i = 0; i < objects.length; i++) {
59                 Object JavaDoc object = objects[i];
60                 switch (event.getChangeType()) {
61                     case IModelChangedEvent.REMOVE :
62                         if (object instanceof IDocumentNode)
63                             removeNode((IDocumentNode) object, ops);
64                         break;
65                     case IModelChangedEvent.INSERT :
66                         if (object instanceof IDocumentNode)
67                             insertNode((IDocumentNode) object, ops);
68                         break;
69                     case IModelChangedEvent.CHANGE :
70                         if (object instanceof IDocumentNode) {
71                             IDocumentNode node = (IDocumentNode) object;
72                             IDocumentAttribute attr = node.getDocumentAttribute(event.getChangedProperty());
73                             if (attr != null) {
74                                 addAttributeOperation(attr, ops, event);
75                             } else if (event.getOldValue() instanceof IDocumentNode && event.getNewValue() instanceof IDocumentNode){
76                                 // swapping of nodes
77
modifyNode(node, ops, event);
78                             }
79                         } else if (object instanceof IDocumentTextNode) {
80                             addElementContentOperation((IDocumentTextNode)object, ops);
81                         }
82                     default:
83                         break;
84                 }
85             }
86         }
87     }
88     
89     private void removeNode(IDocumentNode node, ArrayList JavaDoc ops) {
90         // delete previous op on this node, if any
91
TextEdit old = (TextEdit)fOperationTable.get(node);
92         if (old != null) {
93             ops.remove(old);
94             fOperationTable.remove(node);
95         }
96         TextEdit oldMove= (TextEdit)fMoveOperations.get(node);
97         if (oldMove != null) {
98             ops.remove(oldMove);
99             fMoveOperations.remove(node);
100         }
101         // if node has an offset, delete it
102
if (node.getOffset() > -1) {
103             // Create a delete op for this node
104
TextEdit op = getDeleteNodeOperation(node);
105             ops.add(op);
106             fOperationTable.put(node, op);
107         } else if (old == null && oldMove == null){
108             // No previous op on this non-offset node, just rewrite highest ancestor with an offset
109
insertNode(node, ops);
110         }
111     }
112
113     private void insertNode(IDocumentNode node, ArrayList JavaDoc ops) {
114         TextEdit op = null;
115         node = getHighestNodeToBeWritten(node);
116         if (node.getParentNode() == null) {
117             // https://bugs.eclipse.org/bugs/show_bug.cgi?id=163161
118
if (node.isRoot())
119                 op = new InsertEdit(0, node.write(true));
120         } else {
121             if (node.getOffset() > -1) {
122                 // this is an element that was of the form <element/>
123
// it now needs to be broken up into <element><new/></element>
124
op = new ReplaceEdit(node.getOffset(), node.getLength(), node.write(false));
125             } else {
126                 // try to insert after last sibling that has an offset
127
op = insertAfterSibling(node);
128                 // insert as first child of its parent
129
if (op == null) {
130                     op = insertAsFirstChild(node);
131                 }
132             }
133         }
134         TextEdit old = (TextEdit) fOperationTable.get(node);
135         if (old != null)
136             ops.remove(old);
137         if (op != null) {
138             ops.add(op);
139             fOperationTable.put(node, op);
140         }
141     }
142
143     private InsertEdit insertAfterSibling(IDocumentNode node) {
144         IDocumentNode sibling = node.getPreviousSibling();
145         for (;;) {
146             if (sibling == null)
147                 break;
148             if (sibling.getOffset() > -1) {
149                 node.setLineIndent(sibling.getLineIndent());
150                 String JavaDoc sep = TextUtilities.getDefaultLineDelimiter(getDocumentProvider().getDocument(getInput()));
151                 return new InsertEdit(sibling.getOffset() + sibling.getLength(), sep + node.write(true));
152             }
153             sibling = sibling.getPreviousSibling();
154         }
155         return null;
156     }
157     
158     private InsertEdit insertAsFirstChild(IDocumentNode node) {
159         int offset = node.getParentNode().getOffset();
160         int length = getNextPosition(getDocumentProvider().getDocument(getInput()), offset, '>');
161         node.setLineIndent(node.getParentNode().getLineIndent() + 3);
162         String JavaDoc sep = TextUtilities.getDefaultLineDelimiter(getDocumentProvider().getDocument(getInput()));
163         return new InsertEdit(offset+ length + 1, sep + node.write(true));
164     }
165     
166
167     private void modifyNode(IDocumentNode node, ArrayList JavaDoc ops, IModelChangedEvent event) {
168         IDocumentNode oldNode = (IDocumentNode)event.getOldValue();
169         IDocumentNode newNode = (IDocumentNode)event.getNewValue();
170         
171         IDocumentNode node1 = (oldNode.getPreviousSibling() == null || oldNode.equals(newNode.getPreviousSibling())) ? oldNode : newNode;
172         IDocumentNode node2 = node1.equals(oldNode) ? newNode : oldNode;
173         
174         if (node1.getOffset() < 0 && node2.getOffset() < 2) {
175             TextEdit op = (TextEdit)fOperationTable.get(node1);
176             if (op == null) {
177                 // node 1 has no rule, so node 2 has no rule, therefore rewrite parent/ancestor
178
insertNode(node, ops);
179             } else {
180                 // swap order of insert operations
181
TextEdit op2 = (TextEdit)fOperationTable.get(node2);
182                 ops.set(ops.indexOf(op), op2);
183                 ops.set(ops.indexOf(op2), op);
184             }
185         } else if (node1.getOffset() > -1 && node2.getOffset() > -1) {
186             // both nodes have offsets, so create a move target/source combo operation
187
IRegion region = getMoveRegion(node1);
188             MoveSourceEdit source = new MoveSourceEdit(region.getOffset(), region.getLength());
189             region = getMoveRegion(node2);
190             source.setTargetEdit(new MoveTargetEdit(region.getOffset()));
191             MoveSourceEdit op = (MoveSourceEdit)fMoveOperations.get(node1);
192             if (op != null) {
193                 ops.set(ops.indexOf(op), source);
194             } else {
195                 op = (MoveSourceEdit)fMoveOperations.get(node2);
196                 if (op != null && op.getTargetEdit().getOffset() == source.getOffset()) {
197                     fMoveOperations.remove(node2);
198                     ops.remove(op);
199                     return;
200                 }
201                 ops.add(source);
202             }
203             fMoveOperations.put(node1, source);
204         } else {
205             // one node with offset, the other without offset. Delete/reinsert the one without offset
206
insertNode((node1.getOffset() < 0) ? node1 : node2, ops);
207         }
208     }
209     
210     private IRegion getMoveRegion(IDocumentNode node) {
211         int offset = node.getOffset();
212         int length = node.getLength();
213         int i = 1;
214         try {
215             IDocument doc = getDocumentProvider().getDocument(getInput());
216             for (;;i++) {
217                 char ch = doc.get(offset - i, 1).toCharArray()[0];
218                 if (!Character.isWhitespace(ch)) {
219                     i -= 1;
220                     break;
221                 }
222             }
223         } catch (BadLocationException e) {
224         }
225         return new Region(offset - i, length + i);
226     }
227
228     private void addAttributeOperation(IDocumentAttribute attr, ArrayList JavaDoc ops, IModelChangedEvent event) {
229         int offset = attr.getValueOffset();
230         Object JavaDoc newValue = event.getNewValue();
231         Object JavaDoc changedObject = attr;
232         TextEdit op = null;
233         if (offset > -1) {
234             if (newValue == null || newValue.toString().length() == 0) {
235                 int length = attr.getValueOffset() + attr.getValueLength() + 1 - attr.getNameOffset();
236                 op = getAttributeDeleteEditOperation(attr.getNameOffset(), length);
237             } else {
238                 op = new ReplaceEdit(offset, attr.getValueLength(), getWritableString(event.getNewValue().toString()));
239             }
240         }
241                 
242         if (op == null) {
243             IDocumentNode node = attr.getEnclosingElement();
244             IDocument doc = getDocumentProvider().getDocument(getInput());
245             if (node.getOffset() > -1) {
246                 changedObject = node;
247                 int len = getNextPosition(doc, node.getOffset(), '>');
248                 op = new ReplaceEdit(node.getOffset(), len + 1, node.writeShallow(shouldTerminateElement(doc, node.getOffset() + len)));
249             } else {
250                 insertNode(node, ops);
251                 return;
252             }
253         }
254         TextEdit oldOp = (TextEdit)fOperationTable.get(changedObject);
255         if (oldOp != null)
256             ops.remove(oldOp);
257         ops.add(op);
258         fOperationTable.put(changedObject, op);
259     }
260     
261     private void addElementContentOperation(IDocumentTextNode textNode, ArrayList JavaDoc ops) {
262         TextEdit op = null;
263         Object JavaDoc changedObject = textNode;
264         if (textNode.getOffset() > -1) {
265             String JavaDoc newText = getWritableString(textNode.getText());
266             op = new ReplaceEdit(textNode.getOffset(), textNode.getLength(), newText);
267         } else {
268             IDocumentNode parent = textNode.getEnclosingElement();
269             if (parent.getOffset() > -1) {
270                 IDocument doc = getDocumentProvider().getDocument(getInput());
271                 try {
272                     String JavaDoc endChars = doc.get(parent.getOffset() + parent.getLength() - 2, 2);
273                     if ("/>".equals(endChars)) { //$NON-NLS-1$
274
// parent element is of the form <element/>, rewrite it
275
insertNode(parent, ops);
276                         return;
277                     }
278                 } catch (BadLocationException e) {
279                 }
280                 // add text as first child
281
changedObject = parent;
282                 String JavaDoc sep = TextUtilities.getDefaultLineDelimiter(getDocumentProvider().getDocument(getInput()));
283                 StringBuffer JavaDoc buffer = new StringBuffer JavaDoc(sep);
284                 for (int i = 0; i < parent.getLineIndent(); i++)
285                     buffer.append(" "); //$NON-NLS-1$
286
buffer.append(" " + getWritableString(textNode.getText())); //$NON-NLS-1$
287
int offset = parent.getOffset();
288                 int length = getNextPosition(doc, offset, '>');
289                 op = new InsertEdit(offset+ length + 1, buffer.toString());
290             } else {
291                 insertNode(parent, ops);
292                 return;
293             }
294         }
295         TextEdit oldOp = (TextEdit)fOperationTable.get(changedObject);
296         if (oldOp != null)
297             ops.remove(oldOp);
298         ops.add(op);
299         fOperationTable.put(changedObject, op);
300     }
301
302     private boolean shouldTerminateElement(IDocument doc, int offset) {
303         try {
304             return doc.get(offset-1, 1).toCharArray()[0] == '/';
305         } catch (BadLocationException e) {
306         }
307         return false;
308     }
309     
310     private int getNextPosition(IDocument doc, int offset, char ch) {
311         int i = 0;
312         try {
313             for (i = 0; i + offset < doc.getLength() ;i++) {
314                 if (ch == doc.getChar(offset + i))
315                     break;
316             }
317         } catch (BadLocationException e) {
318         }
319         return i;
320     }
321     
322     private DeleteEdit getAttributeDeleteEditOperation(int offset, int length) {
323         try {
324             IDocument doc = getDocumentProvider().getDocument(getInput());
325             // Traverse backwards in the document starting at the attribute
326
// offset
327
// Goal: Delete all whitespace preceding the attribute name
328
// including spaces, tabs and newlines
329
// We want the next attribute (if defined) to be in the same
330
// position as the deleted one and properly indented. Otherwise,
331
// we want the open angle bracket to be adjacent to the start
332
// element tag name or adjacent to the previous attribute (if
333
// defined) before the deleted one
334
// e.g. _____\n________att1="value1"
335
// This is accomplished by growing the length and decrementing
336
// the offset in order to include the extra whitespace in the
337
// deletion operation
338
for (int i = (offset - 1); i >= 0; i--) {
339                 // Get the character at the specified document index
340
char character = doc.getChar(i);
341                 // If the character is whitespace, include it in the deletion
342
// operation
343
if (Character.isWhitespace(character)) {
344                     // Grow length by one
345
length = length + 1;
346                     // Decrement offset by one
347
offset = offset - 1;
348                 } else {
349                     // Non-whitespace character encountered, do not mark it
350
// for deletion and we are done
351
break;
352                 }
353             }
354         } catch (BadLocationException e) {
355         }
356         return new DeleteEdit(offset, length);
357     }
358     
359
360     private DeleteEdit getDeleteNodeOperation(IDocumentNode node) {
361         int offset = node.getOffset();
362         int length = node.getLength();
363         try {
364             IDocument doc = getDocumentProvider().getDocument(getInput());
365             // node starts on this line:
366
int startLine = doc.getLineOfOffset(offset);
367             // 1st char on startLine has this offset:
368
int startLineOffset = doc.getLineOffset(startLine);
369             // hunt down 1st whitespace/start of line with startOffset:
370
int startOffset;
371             // loop backwards to the beginning of the line, stop if we find non-whitespace
372
for (startOffset = offset - 1; startOffset >= startLineOffset; startOffset -= 1)
373                 if (!Character.isWhitespace(doc.getChar(startOffset)))
374                     break;
375             
376             // move forward one (loop stopped after reaching too far)
377
startOffset += 1;
378             
379             // node ends on this line:
380
int endLine = doc.getLineOfOffset(offset + length);
381             // length of last line's delimiter:
382
int endLineDelimLength = doc.getLineDelimiter(endLine).length();
383             // hunt last whitespace/end of line with extraLength:
384
int extraLength = length;
385             while (true) {
386                 extraLength += 1;
387                 if (!Character.isWhitespace(doc.getChar(offset + extraLength))) {
388                     // found non-white space, move back one
389
extraLength -= 1;
390                     break;
391                 }
392                 if (doc.getLineOfOffset(offset + extraLength) > endLine) {
393                     // don't want to touch the lineDelimeters
394
extraLength -= endLineDelimLength;
395                     break;
396                 }
397             }
398             
399             // if we reached start of line, remove newline
400
if (startOffset == startLineOffset)
401                 startOffset -= doc.getLineDelimiter(startLine).length();
402             
403             // add difference of new offset
404
length = extraLength + (offset - startOffset);
405             offset = startOffset;
406 // printDeletionRange(offset, length);
407
} catch (BadLocationException e) {
408         }
409         return new DeleteEdit(offset, length);
410     }
411
412     protected void printDeletionRange(int offset, int length) {
413         try {
414             // newlines printed as \n
415
// carriage returns printed as \r
416
// tabs printed as \t
417
// spaces printed as *
418
String JavaDoc string = getDocumentProvider().getDocument(getInput()).get(offset, length);
419             StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
420             for (int i = 0; i < string.length(); i++) {
421                 char c = string.charAt(i);
422                 if (c == '\n')
423                     buffer.append("\\n"); //$NON-NLS-1$
424
else if (c == '\r')
425                     buffer.append("\\r"); //$NON-NLS-1$
426
else if (c == '\t')
427                     buffer.append("\\t"); //$NON-NLS-1$
428
else if (c == ' ')
429                     buffer.append('*');
430                 else
431                     buffer.append(c);
432             }
433             System.out.println(buffer.toString());
434         } catch (BadLocationException e) {
435         }
436     }
437     
438     private IDocumentNode getHighestNodeToBeWritten(IDocumentNode node) {
439         IDocumentNode parent = node.getParentNode();
440         if (parent == null)
441             return node;
442         if (parent.getOffset() > -1) {
443             IDocument doc = getDocumentProvider().getDocument(getInput());
444             try {
445                 String JavaDoc endChars = doc.get(parent.getOffset() + parent.getLength() - 2, 2);
446                 return ("/>".equals(endChars)) ? parent : node; //$NON-NLS-1$
447
} catch (BadLocationException e) {
448                 return node;
449             }
450             
451         }
452         return getHighestNodeToBeWritten(parent);
453     }
454     
455     /* (non-Javadoc)
456      * @see org.eclipse.pde.internal.ui.neweditor.context.InputContext#flushModel(org.eclipse.jface.text.IDocument)
457      */

458     protected void flushModel(IDocument doc) {
459         removeUnnecessaryOperations();
460         if (fOperationTable.size() == 1) {
461             Object JavaDoc object = fOperationTable.keySet().iterator().next();
462             if (object instanceof IDocumentNode && fEditOperations.get(0) instanceof InsertEdit) {
463                 if (((IDocumentNode)object).getParentNode() == null) {
464                     doc.set(((IDocumentNode)object).write(true));
465                     fOperationTable.clear();
466                     fEditOperations.clear();
467                     return;
468                 }
469             }
470         }
471         reorderInsertEdits(fEditOperations);
472         fOperationTable.clear();
473         fMoveOperations.clear();
474         super.flushModel(doc);
475     }
476     
477     protected abstract void reorderInsertEdits(ArrayList JavaDoc ops);
478
479     protected void removeUnnecessaryOperations() {
480         Iterator JavaDoc iter = fOperationTable.values().iterator();
481         while (iter.hasNext()) {
482             Object JavaDoc object = iter.next();
483             if (object instanceof IDocumentNode) {
484                 IDocumentNode node = (IDocumentNode)object;
485                 if (node.getOffset() > -1) {
486                     IDocumentAttribute[] attrs = node.getNodeAttributes();
487                     for (int i = 0; i < attrs.length; i++) {
488                         Object JavaDoc op = fOperationTable.remove(attrs[i]);
489                         if (op != null)
490                             fEditOperations.remove(op);
491                     }
492                     IDocumentTextNode textNode = node.getTextNode();
493                     if (textNode != null) {
494                         Object JavaDoc op = fOperationTable.remove(textNode);
495                         if (op != null)
496                             fEditOperations.remove(op);
497                     }
498                 }
499             }
500         }
501     }
502     
503     
504     public String JavaDoc getWritableString(String JavaDoc source) {
505         return PDEXMLHelper.getWritableString(source);
506     }
507     
508     protected HashMap JavaDoc getOperationTable() {
509         return fOperationTable;
510     }
511
512 }
513
Popular Tags