1 19 20 package org.netbeans.modules.xml.text.structure; 21 22 import java.util.ArrayList ; 23 import java.util.Collection ; 24 import java.util.Collections ; 25 import java.util.HashMap ; 26 import java.util.Iterator ; 27 import java.util.List ; 28 import java.util.Map ; 29 import java.util.Stack ; 30 import javax.swing.text.AttributeSet ; 31 import javax.swing.text.BadLocationException ; 32 import org.netbeans.editor.BaseDocument; 33 import org.netbeans.editor.TokenItem; 34 import org.netbeans.modules.editor.structure.api.DocumentElement; 35 import org.netbeans.modules.editor.structure.api.DocumentModel; 36 import org.netbeans.modules.editor.structure.api.DocumentModel.DocumentChange; 37 import org.netbeans.modules.editor.structure.api.DocumentModel.DocumentModelTransactionCancelledException; 38 import org.netbeans.modules.editor.structure.api.DocumentModelException; 39 import org.netbeans.modules.editor.structure.api.DocumentModelUtils; 40 import org.netbeans.modules.editor.structure.spi.DocumentModelProvider; 41 import org.netbeans.modules.xml.text.syntax.SyntaxElement; 42 import org.netbeans.modules.xml.text.syntax.XMLSyntaxSupport; 43 import org.netbeans.modules.xml.text.syntax.XMLTokenIDs; 44 import org.netbeans.modules.xml.text.syntax.dom.AttrImpl; 45 import org.netbeans.modules.xml.text.syntax.dom.CDATASectionImpl; 46 import org.netbeans.modules.xml.text.syntax.dom.CommentImpl; 47 import org.netbeans.modules.xml.text.syntax.dom.DocumentTypeImpl; 48 import org.netbeans.modules.xml.text.syntax.dom.EmptyTag; 49 import org.netbeans.modules.xml.text.syntax.dom.EndTag; 50 import org.netbeans.modules.xml.text.syntax.dom.ProcessingInstructionImpl; 51 import org.netbeans.modules.xml.text.syntax.dom.StartTag; 52 import org.netbeans.modules.xml.text.syntax.dom.Tag; 53 import org.openide.ErrorManager; 54 55 56 60 public class XMLDocumentModelProvider implements DocumentModelProvider { 61 62 63 public void updateModel(DocumentModel.DocumentModelModificationTransaction dtm, 64 DocumentModel model, DocumentChange[] changes) 65 throws DocumentModelException, DocumentModelTransactionCancelledException { 66 67 long a = System.currentTimeMillis(); 68 69 if(debug) System.out.println("\n\n\n\n\n"); 70 if(debug) DocumentModelUtils.dumpElementStructure(model.getRootElement()); 71 72 ArrayList regenerate = new ArrayList (); 74 for(int i = 0; i < changes.length; i++) { 75 DocumentChange dch = changes[i]; 76 77 int changeOffset = dch.getChangeStart().getOffset(); 78 int changeLength = dch.getChangeLength(); 79 80 DocumentElement leaf = model.getLeafElementForOffset(changeOffset); 82 DocumentElement toRegenerate = leaf; 83 84 if(debug) System.out.println(""); 85 if(debug) System.out.println(dch); 86 try { 87 if(debug) System.out.println("inserted text:'" + model.getDocument().getText(changeOffset, changeLength) + "'"); 88 }catch(BadLocationException e) { 89 ; 90 } 91 if(debug) System.out.println("leaf = " + leaf); 92 93 XMLSyntaxSupport sup = (XMLSyntaxSupport)((BaseDocument)model.getDocument()).getSyntaxSupport(); 95 96 boolean textOnly = false; 97 boolean attribsOnly = false; 98 try { 99 TokenItem ti = sup.getTokenChain(changeOffset, changeOffset + 1); 101 while(ti != null && ti.getOffset() < (changeOffset + changeLength)) { 102 if(ti.getTokenID() == XMLTokenIDs.TEXT 103 || ti.getTokenID() == XMLTokenIDs.DECLARATION 104 || ti.getTokenID() == XMLTokenIDs.BLOCK_COMMENT 105 || ti.getTokenID() == XMLTokenIDs.PI_CONTENT 106 || ti.getTokenID() == XMLTokenIDs.CDATA_SECTION) { 107 textOnly = true; 108 break; 109 } 110 if(ti.getTokenID() == XMLTokenIDs.ARGUMENT 111 || ti.getTokenID() == XMLTokenIDs.OPERATOR 112 || ti.getTokenID() == XMLTokenIDs.VALUE) { 113 attribsOnly = true; 114 break; 115 } 116 ti = ti.getNext(); 117 } 118 }catch(BadLocationException e) { 119 ErrorManager.getDefault().notify(ErrorManager.WARNING, e); 120 } 121 122 if(textOnly && 123 ( leaf.getType().equals(XML_CONTENT) 124 || leaf.getType().equals(XML_DOCTYPE) 125 || leaf.getType().equals(XML_PI) 126 || leaf.getType().equals(XML_COMMENT) 127 || leaf.getType().equals(XML_CDATA))){ 128 if(debug) System.out.println("ONLY CONTENT UPDATE!!!"); 131 dtm.updateDocumentElementText(leaf); 132 133 if(dch.getChangeLength() == 1) { 135 continue; 136 } 137 } 138 139 if((attribsOnly || dch.getChangeType() == DocumentChange.REMOVE) 140 && (leaf.getType().equals(XML_TAG) 141 || leaf.getType().equals(XML_EMPTY_TAG))) { 142 if(debug) System.out.println("POSSIBLE ATTRIBS UPDATE!!!"); 143 try { 145 SyntaxElement sel = sup.getElementChain(leaf.getStartOffset() + 1); 146 if(sel instanceof Tag || sel instanceof EmptyTag) { 147 Map newAttrs = createAttributesMap((Tag)sel); 149 AttributeSet existing = leaf.getAttributes(); 150 boolean update = false; 151 if(existing.getAttributeCount() == newAttrs.size()) { 152 Iterator itr = newAttrs.keySet().iterator(); 153 while (itr.hasNext()) { 154 String attrName = (String ) itr.next(); 155 String attrValue = (String )newAttrs.get(attrName); 156 if(attrName != null && attrValue != null 157 && !existing.containsAttribute(attrName, attrValue)) { 158 update = true; 159 break; 160 } 161 162 } 163 } else update = true; 164 165 if(update) { 166 dtm.updateDocumentElementAttribs(leaf, newAttrs); 167 } 168 } 169 }catch(BadLocationException ble) { 170 ErrorManager.getDefault().notify(ErrorManager.WARNING, ble); 171 } 172 } 173 174 if((leaf.getStartOffset() == leaf.getEndOffset()) 176 || (changeOffset == leaf.getStartOffset()) 177 || (changeOffset == leaf.getEndOffset())) 178 toRegenerate = leaf.getParentElement(); 179 else { 180 if(leaf.getType().equals(XML_CONTENT)) { 184 do { 185 toRegenerate = toRegenerate.getParentElement(); 186 } while(toRegenerate != null && toRegenerate.getType().equals(XML_CONTENT)); 187 188 if(toRegenerate == null) { 189 toRegenerate = model.getRootElement(); 191 } 192 } 193 } 194 195 if(toRegenerate == null) toRegenerate = model.getRootElement(); 197 199 Iterator itr = regenerate.iterator(); 202 boolean hasAncestor = false; 203 while(itr.hasNext()) { 204 DocumentElement de = (DocumentElement)itr.next(); 205 if(de.equals(toRegenerate) || model.isDescendantOf(de, toRegenerate)) { 206 hasAncestor = true; 207 break; 208 } 209 } 210 211 if(!hasAncestor) { 212 ArrayList toRemove = new ArrayList (); 215 Iterator i2 = regenerate.iterator(); 216 while(i2.hasNext()) { 217 DocumentElement de = (DocumentElement)i2.next(); 218 if(model.isDescendantOf(toRegenerate, de)) toRemove.add(de); 219 } 220 221 regenerate.removeAll(toRemove); 223 224 regenerate.add(toRegenerate); 226 227 if(debug) System.out.println("==================================================================="); 229 if(debug) System.out.println("change happened in " + leaf); 230 if(debug) System.out.println("we will regenerate its parent " + toRegenerate); 231 } 233 } 235 Iterator elementsToUpdate = regenerate.iterator(); 237 while(elementsToUpdate.hasNext()) { 238 DocumentElement de = (DocumentElement)elementsToUpdate.next(); 239 generateDocumentElements(dtm, model, de); 240 } 241 242 if(measure) System.out.println("[xmlmodel] generated in " + (System.currentTimeMillis() - a)); 243 244 } 245 246 248 private void generateDocumentElements(DocumentModel.DocumentModelModificationTransaction dtm, 249 DocumentModel model, DocumentElement de) throws DocumentModelException, DocumentModelTransactionCancelledException { 250 251 int startOffset = de.getStartOffset(); 252 int endOffset = de.getEndOffset(); 253 254 BaseDocument doc = (BaseDocument)model.getDocument(); 255 XMLSyntaxSupport sup = new XMLSyntaxSupport(doc); 256 257 if(debug) System.out.println("[XMLDocumentModelProvider] regenerating " + de); 258 259 ArrayList addedElements = new ArrayList (); 260 ArrayList skipped = new ArrayList (); 261 try { 262 Stack elementsStack = new Stack (); 264 SyntaxElement sel = sup.getElementChain(Math.min(doc.getLength(), startOffset+1)); 267 268 while(sel != null && getSyntaxElementEndOffset(sel) <= endOffset) { 270 if(sel instanceof SyntaxElement.Error) { 271 if(debug) System.out.println("Error found! => adding error element."); 273 String errorText = doc.getText(sel.getElementOffset(), sel.getElementLength()); 274 addedElements.add(dtm.addDocumentElement(errorText, XML_ERROR, Collections.EMPTY_MAP, 275 sel.getElementOffset(), getSyntaxElementEndOffset(sel))); 276 } 277 278 if(sel instanceof StartTag) { 279 StartTag stag = (StartTag)sel; 281 DocumentElement tagDE = DocumentModelUtils.findElement(model, sel.getElementOffset(), stag.getTagName(), XML_TAG); 282 283 if(tagDE != null && !tagDE.equals(de)) { 285 SyntaxElement endTagCheck = sup.getElementChain(Math.min(doc.getLength(), tagDE.getEndOffset() + 1)); 287 if(endTagCheck instanceof EndTag && ((EndTag)endTagCheck).getTagName().equals(stag.getTagName())) { 288 if(debug) System.out.println("found existing element " + tagDE + " => skipping"); 291 sel = endTagCheck.getNext(); 293 skipped.add(tagDE); 294 continue; 295 } 296 } 297 298 elementsStack.push(sel); 300 301 } else if(sel instanceof EndTag) { 302 if(!elementsStack.isEmpty()) { 303 StartTag latest = (StartTag)elementsStack.peek(); 304 if(((EndTag)sel).getTagName().equals(latest.getTagName())) { 305 Map attribs = createAttributesMap(latest); 307 addedElements.add(dtm.addDocumentElement(latest.getTagName(), XML_TAG, attribs, 308 latest.getElementOffset(), getSyntaxElementEndOffset(sel))); 309 elementsStack.pop(); 311 } else { 312 315 ArrayList savedElements = new ArrayList (); 318 boolean foundStartTag = false; 321 322 while(!elementsStack.isEmpty()) { 323 SyntaxElement s = (SyntaxElement)elementsStack.pop(); 324 savedElements.add(s); 325 326 Tag start = (Tag)s; 327 Tag end = (Tag)sel; 328 329 if(s instanceof StartTag && start.getTagName().equals(end.getTagName())) { 330 Map attribs = createAttributesMap((StartTag)s); 333 addedElements.add(dtm.addDocumentElement(start.getTagName(), XML_TAG, attribs, 334 start.getElementOffset(), getSyntaxElementEndOffset(end))); 335 336 foundStartTag = true; 337 break; } 339 } 340 341 if(!foundStartTag) { 342 for(int i = savedElements.size() - 1; i >= 0; i--) { 345 elementsStack.push(savedElements.get(i)); 346 } 347 } 348 } 349 } 350 } else if(sel instanceof EmptyTag) { 351 Map attribs = createAttributesMap((Tag)sel); 352 addedElements.add(dtm.addDocumentElement(((EmptyTag)sel).getTagName(), XML_EMPTY_TAG, attribs, 353 sel.getElementOffset(), getSyntaxElementEndOffset(sel))); 354 } else if (sel instanceof CDATASectionImpl) { 355 addedElements.add(dtm.addDocumentElement("cdata", XML_CDATA, Collections.EMPTY_MAP, 357 sel.getElementOffset(), getSyntaxElementEndOffset(sel))); 358 } else if (sel instanceof ProcessingInstructionImpl) { 359 String nodeName = ((ProcessingInstructionImpl)sel).getNodeName(); 361 if(nodeName != null) { 363 addedElements.add(dtm.addDocumentElement(nodeName, XML_PI, Collections.EMPTY_MAP, 364 sel.getElementOffset(), getSyntaxElementEndOffset(sel))); 365 } 366 } else if (sel instanceof DocumentTypeImpl) { 367 String nodeName = ((DocumentTypeImpl)sel).getName(); 369 if(nodeName != null) { 371 addedElements.add(dtm.addDocumentElement(nodeName, XML_DOCTYPE, Collections.EMPTY_MAP, 372 sel.getElementOffset(), getSyntaxElementEndOffset(sel))); 373 } 374 } else if (sel instanceof CommentImpl) { 375 addedElements.add(dtm.addDocumentElement("comment", XML_COMMENT, Collections.EMPTY_MAP, 378 sel.getElementOffset(), getSyntaxElementEndOffset(sel))); 379 } else { 380 addedElements.add(dtm.addDocumentElement("...", XML_CONTENT, Collections.EMPTY_MAP, sel.getElementOffset(), getSyntaxElementEndOffset(sel))); 382 } 383 try { 386 SyntaxElement prev = null; 388 int add = 0; 389 do { 390 add++; 391 prev = sup.getElementChain(sel.getElementOffset() + sel.getElementLength() + add); 392 } while(prev != null && sel.getElementOffset() >= prev.getElementOffset()); 393 sel = prev; 394 }catch(BadLocationException ble) { 395 sel = null; 396 } 397 } 398 399 List existingElements = getDescendantsOfNotSkippedElements(de, skipped); 403 existingElements.add(de); 404 405 Iterator existingItr = existingElements.iterator(); 406 while(existingItr.hasNext()) { 408 DocumentElement d = (DocumentElement)existingItr.next(); 409 if(!addedElements.contains(d)) { 410 dtm.removeDocumentElement(d, false); 412 if(debug) System.out.println("[xml model] removed element " + d); 413 } 414 } 415 416 } catch( BadLocationException e ) { 417 throw new DocumentModelException("Error occurred during generation of Document elements", e); 418 } 419 } 420 421 422 private List getDescendantsOfNotSkippedElements(DocumentElement de, List skippedElements) { 423 ArrayList desc = new ArrayList (); 424 Iterator children = de.getChildren().iterator(); 425 while(children.hasNext()) { 426 DocumentElement child = (DocumentElement)children.next(); 427 if(!skippedElements.contains(child)) { 428 desc.add(child); 429 desc.addAll(getDescendantsOfNotSkippedElements(child, skippedElements)); 430 } 431 } 432 return desc; 433 } 434 435 private int getSyntaxElementEndOffset(SyntaxElement sel) { 436 return sel.getElementOffset() + sel.getElementLength() -1; 445 } 446 447 private Map createAttributesMap(Tag tag) { 448 HashMap map = new HashMap (tag.getAttributes().getLength()); 449 for(int i = 0; i < tag.getAttributes().getLength(); i++) { 450 AttrImpl attr = (AttrImpl)tag.getAttributes().item(i); 451 map.put(attr.getName(), attr.getValue()); 452 } 453 return map; 454 } 455 456 public static final String XML_TAG = "tag"; 457 public static final String XML_EMPTY_TAG = "empty_tag"; 458 public static final String XML_CONTENT = "content"; 459 public static final String XML_PI = "pi"; 460 public static final String XML_CDATA = "cdata"; 461 public static final String XML_DOCTYPE = "doctype"; 462 public static final String XML_COMMENT = "comment"; 463 464 public static final String XML_ERROR = "error"; 465 466 467 private static final boolean debug = Boolean.getBoolean("org.netbeans.modules.xml.text.structure.debug"); 468 private static final boolean measure = Boolean.getBoolean("org.netbeans.modules.xml.text.structure.measure"); 469 470 } 471 | Popular Tags |