1 11 package org.eclipse.pde.internal.ui.editor.context; 12 import java.util.ArrayList ; 13 import java.util.HashMap ; 14 import java.util.Iterator ; 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 fOperationTable = new HashMap (); 38 protected HashMap fMoveOperations = new HashMap (); 39 40 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 55 protected void addTextEditOperation(ArrayList ops, IModelChangedEvent event) { 56 Object [] objects = event.getChangedObjects(); 57 if (objects != null) { 58 for (int i = 0; i < objects.length; i++) { 59 Object 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 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 ops) { 90 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.getOffset() > -1) { 103 TextEdit op = getDeleteNodeOperation(node); 105 ops.add(op); 106 fOperationTable.put(node, op); 107 } else if (old == null && oldMove == null){ 108 insertNode(node, ops); 110 } 111 } 112 113 private void insertNode(IDocumentNode node, ArrayList ops) { 114 TextEdit op = null; 115 node = getHighestNodeToBeWritten(node); 116 if (node.getParentNode() == null) { 117 if (node.isRoot()) 119 op = new InsertEdit(0, node.write(true)); 120 } else { 121 if (node.getOffset() > -1) { 122 op = new ReplaceEdit(node.getOffset(), node.getLength(), node.write(false)); 125 } else { 126 op = insertAfterSibling(node); 128 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 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 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 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 insertNode(node, ops); 179 } else { 180 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 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 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 ops, IModelChangedEvent event) { 229 int offset = attr.getValueOffset(); 230 Object newValue = event.getNewValue(); 231 Object 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 ops) { 262 TextEdit op = null; 263 Object changedObject = textNode; 264 if (textNode.getOffset() > -1) { 265 String 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 endChars = doc.get(parent.getOffset() + parent.getLength() - 2, 2); 273 if ("/>".equals(endChars)) { insertNode(parent, ops); 276 return; 277 } 278 } catch (BadLocationException e) { 279 } 280 changedObject = parent; 282 String sep = TextUtilities.getDefaultLineDelimiter(getDocumentProvider().getDocument(getInput())); 283 StringBuffer buffer = new StringBuffer (sep); 284 for (int i = 0; i < parent.getLineIndent(); i++) 285 buffer.append(" "); buffer.append(" " + getWritableString(textNode.getText())); 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 for (int i = (offset - 1); i >= 0; i--) { 339 char character = doc.getChar(i); 341 if (Character.isWhitespace(character)) { 344 length = length + 1; 346 offset = offset - 1; 348 } else { 349 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 int startLine = doc.getLineOfOffset(offset); 367 int startLineOffset = doc.getLineOffset(startLine); 369 int startOffset; 371 for (startOffset = offset - 1; startOffset >= startLineOffset; startOffset -= 1) 373 if (!Character.isWhitespace(doc.getChar(startOffset))) 374 break; 375 376 startOffset += 1; 378 379 int endLine = doc.getLineOfOffset(offset + length); 381 int endLineDelimLength = doc.getLineDelimiter(endLine).length(); 383 int extraLength = length; 385 while (true) { 386 extraLength += 1; 387 if (!Character.isWhitespace(doc.getChar(offset + extraLength))) { 388 extraLength -= 1; 390 break; 391 } 392 if (doc.getLineOfOffset(offset + extraLength) > endLine) { 393 extraLength -= endLineDelimLength; 395 break; 396 } 397 } 398 399 if (startOffset == startLineOffset) 401 startOffset -= doc.getLineDelimiter(startLine).length(); 402 403 length = extraLength + (offset - startOffset); 405 offset = startOffset; 406 } catch (BadLocationException e) { 408 } 409 return new DeleteEdit(offset, length); 410 } 411 412 protected void printDeletionRange(int offset, int length) { 413 try { 414 String string = getDocumentProvider().getDocument(getInput()).get(offset, length); 419 StringBuffer buffer = new StringBuffer (); 420 for (int i = 0; i < string.length(); i++) { 421 char c = string.charAt(i); 422 if (c == '\n') 423 buffer.append("\\n"); else if (c == '\r') 425 buffer.append("\\r"); else if (c == '\t') 427 buffer.append("\\t"); 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 endChars = doc.get(parent.getOffset() + parent.getLength() - 2, 2); 446 return ("/>".equals(endChars)) ? parent : node; } catch (BadLocationException e) { 448 return node; 449 } 450 451 } 452 return getHighestNodeToBeWritten(parent); 453 } 454 455 458 protected void flushModel(IDocument doc) { 459 removeUnnecessaryOperations(); 460 if (fOperationTable.size() == 1) { 461 Object 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 ops); 478 479 protected void removeUnnecessaryOperations() { 480 Iterator iter = fOperationTable.values().iterator(); 481 while (iter.hasNext()) { 482 Object 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 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 op = fOperationTable.remove(textNode); 495 if (op != null) 496 fEditOperations.remove(op); 497 } 498 } 499 } 500 } 501 } 502 503 504 public String getWritableString(String source) { 505 return PDEXMLHelper.getWritableString(source); 506 } 507 508 protected HashMap getOperationTable() { 509 return fOperationTable; 510 } 511 512 } 513 | Popular Tags |