1 11 package org.eclipse.pde.internal.core.text.plugin; 12 13 import java.util.ArrayList ; 14 15 import org.eclipse.jface.text.BadLocationException; 16 import org.eclipse.jface.text.IDocument; 17 import org.eclipse.jface.text.IRegion; 18 import org.eclipse.jface.text.Region; 19 import org.eclipse.jface.text.TextUtilities; 20 import org.eclipse.pde.core.IModelChangedEvent; 21 import org.eclipse.pde.internal.core.text.AbstractTextChangeListener; 22 import org.eclipse.pde.internal.core.text.IDocumentAttribute; 23 import org.eclipse.pde.internal.core.text.IDocumentNode; 24 import org.eclipse.pde.internal.core.text.IDocumentTextNode; 25 import org.eclipse.pde.internal.core.util.PDEXMLHelper; 26 import org.eclipse.pde.internal.core.util.PropertiesUtil; 27 import org.eclipse.text.edits.DeleteEdit; 28 import org.eclipse.text.edits.InsertEdit; 29 import org.eclipse.text.edits.MoveSourceEdit; 30 import org.eclipse.text.edits.MoveTargetEdit; 31 import org.eclipse.text.edits.MultiTextEdit; 32 import org.eclipse.text.edits.ReplaceEdit; 33 import org.eclipse.text.edits.TextEdit; 34 35 public class XMLTextChangeListener extends AbstractTextChangeListener { 36 37 private ArrayList fOperationList = new ArrayList (); 38 39 public XMLTextChangeListener(IDocument document) { 40 super(document); 41 } 42 43 public TextEdit[] getTextOperations() { 44 if (fOperationList.size() == 0) 45 return new TextEdit[0]; 46 47 MultiTextEdit edit = new MultiTextEdit(); 48 try { 49 if (PropertiesUtil.isNewlineNeeded(fDocument)) 50 insert(edit, new InsertEdit(fDocument.getLength(), TextUtilities.getDefaultLineDelimiter(fDocument))); 51 } catch (BadLocationException e) { 52 } 53 Object [] operations = fOperationList.toArray(); 54 for (int i = 0; i < operations.length; i++) 55 insert(edit, (TextEdit)operations[i]); 56 57 return new TextEdit[] {edit}; 58 } 59 60 protected static void insert(TextEdit parent, TextEdit edit) { 61 if (!parent.hasChildren()) { 62 parent.addChild(edit); 63 if (edit instanceof MoveSourceEdit) { 64 parent.addChild(((MoveSourceEdit)edit).getTargetEdit()); 65 } 66 return; 67 } 68 TextEdit[] children= parent.getChildren(); 69 for (int i= 0; i < children.length; i++) { 71 TextEdit child= children[i]; 72 if (covers(child, edit)) { 73 insert(child, edit); 74 return; 75 } 76 } 77 for (int i= children.length - 1; i >= 0; i--) { 80 TextEdit child= children[i]; 81 if (covers(edit, child)) { 82 parent.removeChild(i); 83 edit.addChild(child); 84 } 85 } 86 parent.addChild(edit); 87 if (edit instanceof MoveSourceEdit) { 88 parent.addChild(((MoveSourceEdit)edit).getTargetEdit()); 89 } 90 } 91 92 protected static boolean covers(TextEdit thisEdit, TextEdit otherEdit) { 93 if (thisEdit.getLength() == 0) return false; 95 96 int thisOffset= thisEdit.getOffset(); 97 int thisEnd= thisEdit.getExclusiveEnd(); 98 if (otherEdit.getLength() == 0) { 99 int otherOffset= otherEdit.getOffset(); 100 return thisOffset < otherOffset && otherOffset < thisEnd; 101 } 102 int otherOffset= otherEdit.getOffset(); 103 int otherEnd= otherEdit.getExclusiveEnd(); 104 return thisOffset <= otherOffset && otherEnd <= thisEnd; 105 } 106 107 108 protected void deleteNode(IDocumentNode node) { 109 TextEdit old = (TextEdit)fOperationTable.get(node); 111 if (old != null) { 112 Object op = fOperationTable.remove(node); 113 fOperationList.remove(op); 114 } 115 116 if (node.getOffset() > -1) { 118 TextEdit op = getDeleteNodeOperation(node); 120 fOperationTable.put(node, op); 121 fOperationList.add(op); 122 } else if (old == null){ 123 insertNode(node); 125 } 126 } 127 128 protected void insertNode(IDocumentNode node) { 129 TextEdit op = null; 130 node = getHighestNodeToBeWritten(node); 131 if (node.getParentNode() == null) { 132 if (node.isRoot()) { 137 op = new InsertEdit(0, node.write(true)); 138 } 139 } else { 140 if (node.getOffset() > -1) { 141 op = new ReplaceEdit(node.getOffset(), node.getLength(), node.write(false)); 144 } else { 145 op = insertAfterSibling(node); 147 if (op == null) { 149 op = insertAsFirstChild(node); 150 } 151 } 152 } 153 fOperationTable.put(node, op); 154 fOperationList.add(op); 155 } 156 157 private InsertEdit insertAfterSibling(IDocumentNode node) { 158 IDocumentNode sibling = node.getPreviousSibling(); 159 for (;;) { 160 if (sibling == null) 161 break; 162 if (sibling.getOffset() > -1) { 163 node.setLineIndent(sibling.getLineIndent()); 164 return new InsertEdit(sibling.getOffset() + sibling.getLength(), fSep + node.write(true)); 165 } 166 sibling = sibling.getPreviousSibling(); 167 } 168 return null; 169 } 170 171 private InsertEdit insertAsFirstChild(IDocumentNode node) { 172 int offset = node.getParentNode().getOffset(); 173 int length = getNextPosition(fDocument, offset, '>'); 174 node.setLineIndent(node.getParentNode().getLineIndent() + 3); 175 return new InsertEdit(offset+ length + 1, fSep + node.write(true)); 176 } 177 178 179 protected void modifyNode(IDocumentNode node, IModelChangedEvent event) { 180 IDocumentNode oldNode = (IDocumentNode)event.getOldValue(); 181 IDocumentNode newNode = (IDocumentNode)event.getNewValue(); 182 183 IDocumentNode node1 = (oldNode.getPreviousSibling() == null || oldNode.equals(newNode.getPreviousSibling())) ? oldNode : newNode; 184 IDocumentNode node2 = node1.equals(oldNode) ? newNode : oldNode; 185 186 if (node1.getOffset() < 0 && node2.getOffset() < 0) { 187 TextEdit op = (TextEdit)fOperationTable.get(node1); 188 if (op == null) { 189 insertNode(node); 191 } 192 } else if (node1.getOffset() > -1 && node2.getOffset() > -1) { 193 IRegion region = getMoveRegion(node1); 195 MoveSourceEdit source = new MoveSourceEdit(region.getOffset(), region.getLength()); 196 region = getMoveRegion(node2); 197 source.setTargetEdit(new MoveTargetEdit(region.getOffset())); 198 fOperationTable.put(node, source); 199 fOperationList.add(source); 200 } else { 201 insertNode((node1.getOffset() < 0) ? node1 : node2); 203 } 204 } 205 206 private IRegion getMoveRegion(IDocumentNode node) { 207 int offset = node.getOffset(); 208 int length = node.getLength(); 209 int i = 1; 210 try { 211 for (;;i++) { 212 char ch = fDocument.get(offset - i, 1).toCharArray()[0]; 213 if (!Character.isWhitespace(ch)) { 214 i -= 1; 215 break; 216 } 217 } 218 } catch (BadLocationException e) { 219 } 220 return new Region(offset - i, length + i); 221 } 222 223 protected void addAttributeOperation(IDocumentAttribute attr, IModelChangedEvent event) { 224 int offset = attr.getValueOffset(); 225 Object newValue = event.getNewValue(); 226 Object changedObject = attr; 227 TextEdit op = null; 228 if (offset > -1) { 229 if (newValue == null || newValue.toString().length() == 0) { 230 int length = attr.getValueOffset() + attr.getValueLength() + 1 - attr.getNameOffset(); 231 op = getAttributeDeleteEditOperation(attr.getNameOffset(), length); 232 } else { 233 op = new ReplaceEdit(offset, attr.getValueLength(), getWritableString(event.getNewValue().toString())); 234 } 235 } 236 237 if (op == null) { 238 IDocumentNode node = attr.getEnclosingElement(); 239 if (node.getOffset() > -1) { 240 changedObject = node; 241 int len = getNextPosition(fDocument, node.getOffset(), '>'); 242 op = new ReplaceEdit(node.getOffset(), len + 1, node.writeShallow(shouldTerminateElement(fDocument, node.getOffset() + len))); 243 } else { 244 insertNode(node); 245 return; 246 } 247 } 248 fOperationTable.put(changedObject, op); 249 fOperationList.add(op); 250 } 251 252 protected void addElementContentOperation(IDocumentTextNode textNode) { 253 TextEdit op = null; 254 Object changedObject = textNode; 255 if (textNode.getOffset() > -1) { 256 String newText = getWritableString(textNode.getText()); 257 op = new ReplaceEdit(textNode.getOffset(), textNode.getLength(), newText); 258 } else { 259 IDocumentNode parent = textNode.getEnclosingElement(); 260 if (parent.getOffset() > -1) { 261 try { 262 String endChars = fDocument.get(parent.getOffset() + parent.getLength() - 2, 2); 263 if ("/>".equals(endChars)) { insertNode(parent); 266 return; 267 } 268 } catch (BadLocationException e) { 269 } 270 changedObject = parent; 272 StringBuffer buffer = new StringBuffer (fSep); 273 for (int i = 0; i < parent.getLineIndent(); i++) 274 buffer.append(" "); buffer.append(" " + getWritableString(textNode.getText())); int offset = parent.getOffset(); 277 int length = getNextPosition(fDocument, offset, '>'); 278 op = new InsertEdit(offset+ length + 1, buffer.toString()); 279 } else { 280 insertNode(parent); 281 return; 282 } 283 } 284 fOperationTable.put(changedObject, op); 285 fOperationList.add(op); 286 } 287 288 private boolean shouldTerminateElement(IDocument doc, int offset) { 289 try { 290 return doc.get(offset-1, 1).toCharArray()[0] == '/'; 291 } catch (BadLocationException e) { 292 } 293 return false; 294 } 295 296 private int getNextPosition(IDocument doc, int offset, char ch) { 297 int i = 0; 298 try { 299 for (i = 0; i + offset < doc.getLength() ;i++) { 300 if (ch == doc.get(offset + i, 1).toCharArray()[0]) 301 break; 302 } 303 } catch (BadLocationException e) { 304 } 305 return i; 306 } 307 308 private DeleteEdit getAttributeDeleteEditOperation(int offset, int length) { 309 try { 310 for (;;) { 311 char ch = fDocument.get(offset + length, 1).toCharArray()[0]; 312 if (!Character.isWhitespace(ch)) { 313 break; 314 } 315 316 length += 1; 317 } 318 } catch (BadLocationException e) { 319 } 320 return new DeleteEdit(offset, length); 321 } 322 323 324 private DeleteEdit getDeleteNodeOperation(IDocumentNode node) { 325 int offset = node.getOffset(); 326 int length = node.getLength(); 327 try { 328 int startLine = fDocument.getLineOfOffset(offset); 330 int startLineOffset = fDocument.getLineOffset(startLine); 332 int startOffset; 334 for (startOffset = offset - 1; startOffset >= startLineOffset; startOffset -= 1) 336 if (!Character.isWhitespace(fDocument.getChar(startOffset))) 337 break; 338 339 startOffset += 1; 341 342 int endLine = fDocument.getLineOfOffset(offset + length); 344 int endLineDelimLength = fDocument.getLineDelimiter(endLine).length(); 346 int extraLength = length; 348 while (true) { 349 extraLength += 1; 350 if (!Character.isWhitespace(fDocument.getChar(offset + extraLength))) { 351 extraLength -= 1; 353 break; 354 } 355 if (fDocument.getLineOfOffset(offset + extraLength) > endLine) { 356 extraLength -= endLineDelimLength; 358 break; 359 } 360 } 361 362 if (startOffset == startLineOffset) 364 startOffset -= fDocument.getLineDelimiter(startLine).length(); 365 366 length = extraLength + (offset - startOffset); 368 offset = startOffset; 369 } catch (BadLocationException e) { 371 } 372 return new DeleteEdit(offset, length); 373 } 374 375 protected void printDeletionRange(int offset, int length) { 376 try { 377 String string = fDocument.get(offset, length); 382 StringBuffer buffer = new StringBuffer (); 383 for (int i = 0; i < string.length(); i++) { 384 char c = string.charAt(i); 385 if (c == '\n') 386 buffer.append("\\n"); else if (c == '\r') 388 buffer.append("\\r"); else if (c == '\t') 390 buffer.append("\\t"); else if (c == ' ') 392 buffer.append('*'); 393 else 394 buffer.append(c); 395 } 396 System.out.println(buffer.toString()); 397 } catch (BadLocationException e) { 398 } 399 } 400 401 private IDocumentNode getHighestNodeToBeWritten(IDocumentNode node) { 402 IDocumentNode parent = node.getParentNode(); 403 if (parent == null) 404 return node; 405 if (parent.getOffset() > -1) { 406 try { 407 String endChars = fDocument.get(parent.getOffset() + parent.getLength() - 2, 2); 408 return ("/>".equals(endChars)) ? parent : node; } catch (BadLocationException e) { 410 return node; 411 } 412 413 } 414 return getHighestNodeToBeWritten(parent); 415 } 416 417 private String getWritableString(String source) { 418 return PDEXMLHelper.getWritableString(source); 419 } 420 421 public void modelChanged(IModelChangedEvent event) { 422 Object [] objects = event.getChangedObjects(); 423 if (objects == null) 424 return; 425 for (int i = 0; i < objects.length; i++) { 426 if (!(objects[i] instanceof IDocumentNode)) 427 continue; 428 IDocumentNode node = (IDocumentNode)objects[i]; 429 Object op = fOperationTable.remove(node); 430 fOperationList.remove(op); 431 switch (event.getChangeType()) { 432 case IModelChangedEvent.REMOVE: 433 deleteNode(node); 434 break; 435 case IModelChangedEvent.INSERT: 436 insertNode(node); 437 break; 438 case IModelChangedEvent.CHANGE: 439 IDocumentAttribute attr = node.getDocumentAttribute(event.getChangedProperty()); 440 if (attr != null) { 441 addAttributeOperation(attr, event); 442 } else { 443 if (event.getOldValue() instanceof IDocumentTextNode) { 444 addElementContentOperation((IDocumentTextNode)event.getOldValue()); 445 } else if (event.getOldValue() instanceof IDocumentNode && event.getNewValue() instanceof IDocumentNode){ 446 modifyNode(node, event); 448 } 449 } 450 } 451 } 452 } 453 } 454 | Popular Tags |