PageRenderTime 51ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/bundles/plugins-trunk/XML/xml/XmlParsedData.java

#
Java | 858 lines | 596 code | 104 blank | 158 comment | 153 complexity | e208092a91b8622efc009141ebe6a955 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. /*
  2. * XmlParsedData.java
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 2003 Slava Pestov
  7. *
  8. * The XML plugin is licensed under the GNU General Public License, with
  9. * the following exception:
  10. *
  11. * "Permission is granted to link this code with software released under
  12. * the Apache license version 1.1, for example used by the Xerces XML
  13. * parser package."
  14. */
  15. package xml;
  16. //{{{ Imports
  17. import java.util.ArrayList;
  18. import java.util.Collections;
  19. import java.util.Comparator;
  20. import java.util.HashMap;
  21. import java.util.Iterator;
  22. import java.util.LinkedList;
  23. import java.util.List;
  24. import java.util.Map;
  25. import java.util.TreeMap;
  26. import javax.swing.SwingUtilities;
  27. import javax.swing.tree.DefaultMutableTreeNode;
  28. import javax.swing.tree.TreePath;
  29. import javax.swing.tree.TreeNode;
  30. import org.gjt.sp.jedit.Buffer;
  31. import org.gjt.sp.jedit.EditBus;
  32. import org.gjt.sp.jedit.GUIUtilities;
  33. import org.gjt.sp.util.Log;
  34. import org.gjt.sp.util.StandardUtilities;
  35. import org.gjt.sp.jedit.View;
  36. import sidekick.SideKickParsedData;
  37. import sidekick.ExpansionModel;
  38. import sidekick.SideKickUpdate;
  39. import sidekick.IAsset;
  40. import xml.completion.CompletionInfo;
  41. import xml.completion.ElementDecl;
  42. import xml.completion.EntityDecl;
  43. import xml.completion.IDDecl;
  44. import xml.parser.TagParser;
  45. import xml.parser.XmlTag;
  46. import com.thaiopensource.xml.util.Name;
  47. import java.util.Enumeration;
  48. //}}}
  49. /**
  50. * Encapsulates the results of parsing a buffer, either using Xerces or the
  51. * Swing HTML parser.
  52. */
  53. public class XmlParsedData extends SideKickParsedData
  54. {
  55. // sorting values
  56. public static final int SORT_BY_NAME = 0;
  57. public static final int SORT_BY_LINE = 1;
  58. public static final int SORT_BY_TYPE = 2;
  59. private static int sortBy = SORT_BY_LINE;
  60. protected static boolean sortDown = true;
  61. public boolean html;
  62. /** indicate that all xmlns: attributes appear only on the root element
  63. * so there's no need to find the exact namespace context of the parent node.
  64. * the namespace context of the root element is sufficient
  65. */
  66. public boolean allNamespacesBindingsAtTop;
  67. /**
  68. * A mapping of namespace to CompletionInfo objects.
  69. * namespace of "" is the default namespace.
  70. */
  71. private Map<String, CompletionInfo> mappings;
  72. /**
  73. * A map of all identifiers encountered during the parse,
  74. * indexed by name.
  75. * Used in XMLInsert IDs pane and for Hyperlinks navigation
  76. */
  77. public Map<String,IDDecl> ids;
  78. public List<EntityDecl> entities;
  79. public Map entityHash;
  80. /** entities are added to the noNamespaceCompletionInfo, so if a schema is used
  81. * on top of DTD, the entities are lost.
  82. * To prevent this, the entities are copied into the parsed data
  83. */
  84. public void setCompletionInfo(String namespace, CompletionInfo info) {
  85. if(namespace == null)namespace = "";
  86. mappings.put(namespace, info);
  87. // if this append, should remove entities declared in this CompletionInfo
  88. // and nowhere else.
  89. if(info == null)throw new UnsupportedOperationException("setCompletionInfo("+namespace+",null");
  90. for(EntityDecl en : info.entities)
  91. {
  92. // avoid duplicates of &lt;, &amp; etc.
  93. if(!entityHash.containsKey(en.name)) addEntity(en);
  94. }
  95. }
  96. //{{{ XmlParsedData constructor
  97. public XmlParsedData(String fileName, boolean html)
  98. {
  99. super(fileName);
  100. this.html = html;
  101. mappings = new HashMap<String, CompletionInfo>();
  102. ids = new TreeMap<String, IDDecl>(new Comparator<String>(){
  103. public int compare(String s1,String s2){
  104. return StandardUtilities.compareStrings(s1,s2,false);
  105. }
  106. });
  107. allNamespacesBindingsAtTop = true;
  108. entities = new ArrayList<EntityDecl>();
  109. entityHash = new HashMap();
  110. setCompletionInfo("",getNoNamespaceCompletionInfo());//register NoNamespaceCompletionInfo at least once for the common entities
  111. } //}}}
  112. //{{{ getNoNamespaceCompletionInfo() method
  113. public CompletionInfo getNoNamespaceCompletionInfo()
  114. {
  115. CompletionInfo info = mappings.get("");
  116. if(info == null)
  117. {
  118. info = new CompletionInfo();
  119. mappings.put("",info);
  120. }
  121. return info;
  122. } //}}}
  123. //{{{ getCompletionInfo(namespace) method
  124. public CompletionInfo getCompletionInfo(String ns)
  125. {
  126. return mappings.get(ns);
  127. } //}}}
  128. //{{{ getElementDecl(String,int) method
  129. public ElementDecl getElementDecl(String name, int pos)
  130. {
  131. if(Debug.DEBUG_COMPLETION)Log.log(Log.DEBUG,XmlParsedData.class,
  132. "getElementDecl("+name+","+pos+")");
  133. ElementDecl decl = null;
  134. String prefix = getElementNamePrefix(name);
  135. String localName = "".equals(prefix) ? name : name.substring(prefix.length()+1);
  136. if(html)
  137. {
  138. decl = getElementDeclHTML(name,pos);
  139. }
  140. else
  141. {
  142. TreePath path = null;
  143. Map<String,String> bindings = null;
  144. if(allNamespacesBindingsAtTop){
  145. if(Debug.DEBUG_COMPLETION)Log.log(Log.DEBUG,XmlParsedData.class,
  146. "allNamespacesBindingsAtTop");
  147. bindings = getRootNamespaceBindings();
  148. }else {
  149. path = getTreePathForPosition(pos);
  150. bindings = getNamespaceBindings(path);
  151. }
  152. // find parent's CompletionInfo
  153. CompletionInfo info;
  154. if(mappings.isEmpty()){
  155. if(Debug.DEBUG_COMPLETION)Log.log(Log.DEBUG,XmlParsedData.class,
  156. "no completionInfo");
  157. return null;
  158. }else if(mappings.size() == 1){
  159. info = mappings.values().iterator().next();
  160. if(Debug.DEBUG_COMPLETION)Log.log(Log.DEBUG,XmlParsedData.class,
  161. "only 1 completionInfo, for ns "+info.namespace);
  162. }else{
  163. String NS = getNS(bindings,prefix);
  164. info = mappings.get(NS);
  165. if(Debug.DEBUG_COMPLETION)Log.log(Log.DEBUG,XmlParsedData.class,
  166. "many completionInfos ("+mappings.keySet()+"); getting for NS ="+NS);
  167. }
  168. if(info == null){
  169. if(Debug.DEBUG_COMPLETION)Log.log(Log.DEBUG,XmlParsedData.class,
  170. "CompletionInfo not found");
  171. }else {
  172. if(Debug.DEBUG_COMPLETION)Log.log(Log.DEBUG,XmlParsedData.class,
  173. "got CompletionInfo for "+info.namespace);
  174. // find elementdecl inside CompletionInfo
  175. if(info.nameConflict){
  176. if(path == null)path = getTreePathForPosition(pos);
  177. // find the elements tree leading to parent in the document
  178. DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode)path.getLastPathComponent();
  179. XmlTag parentXmlTag = (XmlTag)parentNode.getUserObject();
  180. if(Debug.DEBUG_COMPLETION)Log.log(Log.DEBUG,XmlParsedData.class,
  181. "parentXmlTag="+parentXmlTag.getName());
  182. // SideKick tree is in sync
  183. if(name.equals(parentXmlTag.getName()))
  184. {
  185. if(Debug.DEBUG_COMPLETION)Log.log(Log.DEBUG,XmlParsedData.class,
  186. "SideKick tree is in sync");
  187. decl = getElementDeclFromSideKick(parentNode,parentXmlTag);
  188. }
  189. else
  190. {
  191. // it's not the parent ; let's say it's an ancestor
  192. /* wrong approach : don't know which one to return.
  193. So either
  194. - keep the Sidekick tree in sync (ie Parse on Keystroke) !
  195. - parse backward to reconstruct ancestry to the last Sidekick node
  196. Don't do this :
  197. ElementDecl ancestorDecl = getElementDecl(parentNode,parentXmlTag);
  198. String NS = getNS(bindings,prefix);
  199. decl = findElementDeclInDescendants(ancestorDecl,NS,localName, new ArrayList<ElementDecl>());
  200. */
  201. if(Debug.DEBUG_COMPLETION)Log.log(Log.DEBUG,XmlParsedData.class,
  202. "Sidekick tree out of sync ; giving up");
  203. decl = null;
  204. }
  205. }else{
  206. if(Debug.DEBUG_COMPLETION)Log.log(Log.DEBUG,XmlParsedData.class,
  207. "no nameConflict");
  208. if(info.elementHash.containsKey(localName)){
  209. if(Debug.DEBUG_COMPLETION)Log.log(Log.DEBUG,XmlParsedData.class,
  210. "global declaration");
  211. decl = info.elementHash.get(localName);
  212. }else{
  213. if(Debug.DEBUG_COMPLETION)Log.log(Log.DEBUG,XmlParsedData.class,
  214. "local declaration");
  215. decl = info.getElementDeclLocal(localName);
  216. }
  217. }
  218. }
  219. }
  220. return decl;
  221. }
  222. //}}}
  223. //{{{ getElementDeclInternal(name, pos) method
  224. /**
  225. * finds a global declaration of an element, returns it without prefix
  226. * @param name qualified name (with prefix) of an element
  227. * @param pos used to get the namespace bindings
  228. */
  229. private ElementDecl getElementDeclHTML(String name,int pos)
  230. {
  231. if(html)
  232. name = name.toLowerCase();
  233. String prefix = getElementNamePrefix(name);
  234. CompletionInfo info;
  235. if(mappings.isEmpty())
  236. {
  237. return null;
  238. }
  239. // simple case where we don't need to get the namespace
  240. else if(mappings.size() == 1)
  241. {
  242. info = mappings.values().iterator().next();
  243. }
  244. else
  245. {
  246. String ns = getNamespaceForPrefix(prefix,pos);
  247. info = mappings.get(ns);
  248. }
  249. if(info == null)
  250. return null;
  251. else
  252. {
  253. String lName = getElementLocalName(name);
  254. ElementDecl decl = info.elementHash.get(lName);
  255. if(decl == null)
  256. return null;
  257. else
  258. return decl;
  259. }
  260. } //}}}
  261. //{{{ getElementDecl() method
  262. private ElementDecl getElementDeclFromSideKick(DefaultMutableTreeNode node,XmlTag tag)
  263. {
  264. if(Debug.DEBUG_COMPLETION)Log.log(Log.DEBUG,XmlParsedData.class,
  265. "getElementDecl("+node+","+tag.getName()+")");
  266. CompletionInfo info = mappings.get(tag.namespace);
  267. if(info == null)
  268. return null;
  269. else
  270. {
  271. String prefix = tag.getPrefix();
  272. String lName = tag.getLocalName();
  273. if(!info.nameConflict && info.elementHash != null) {
  274. return info.elementHash.get(lName);
  275. }
  276. else
  277. {
  278. DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) node.getParent();
  279. if(parentNode.getUserObject() instanceof XmlTag)
  280. {
  281. XmlTag parentTag = (XmlTag)parentNode.getUserObject();
  282. ElementDecl parentDecl = getElementDeclFromSideKick(parentNode,parentTag);
  283. if(parentDecl != null && parentDecl.elementHash!=null && parentDecl.elementHash.containsKey(lName))
  284. {
  285. return parentDecl.elementHash.get(lName);
  286. }
  287. }
  288. else if(info.elementHash != null)
  289. {
  290. // at root node : allowed to use a global ElementDecl
  291. return info.elementHash.get(lName);
  292. }
  293. return null;
  294. }
  295. }
  296. } //}}}
  297. //{{{ getXPathForPosition() method
  298. public String getXPathForPosition(int pos)
  299. {
  300. TreePath path = getTreePathForPosition(pos);
  301. if(path == null)return null;
  302. DefaultMutableTreeNode tn = (DefaultMutableTreeNode)path.getLastPathComponent();
  303. TreeNode[]steps = tn.getPath();
  304. DefaultMutableTreeNode parent = (DefaultMutableTreeNode)steps[0];
  305. StringBuilder xpath = new StringBuilder();
  306. if(steps.length == 1)
  307. {
  308. //there is only the node with the file name
  309. xpath = null;
  310. }
  311. else
  312. {
  313. parent = (DefaultMutableTreeNode)steps[1];
  314. if( ! (parent.getUserObject() instanceof XmlTag))
  315. {
  316. return null;
  317. }
  318. Map<String,String> prefixToNS = new HashMap<String,String>();
  319. Map<String,String> nsToPrefix = new HashMap<String,String>();
  320. Name[] preXPath = new Name[steps.length-2];
  321. int[] preXPathIndexes = new int[steps.length-2];
  322. Name rootName;
  323. XmlTag curTag = (XmlTag)parent.getUserObject();
  324. String lname = curTag.getLocalName();
  325. String ns = curTag.namespace;
  326. StringBuilder prefix = new StringBuilder(curTag.getPrefix());
  327. assert(ns != null);
  328. rootName = new Name(ns,lname);
  329. prefixToNS.put(prefix.toString(), ns);
  330. nsToPrefix.put(ns, prefix.toString());
  331. for(int i=2;i<steps.length;i++)
  332. {
  333. DefaultMutableTreeNode cur=(DefaultMutableTreeNode)steps[i];
  334. curTag = (XmlTag)cur.getUserObject();
  335. ns = curTag.namespace;
  336. lname = curTag.getLocalName();
  337. prefix = new StringBuilder(curTag.getPrefix());
  338. int jCur = parent.getIndex(cur);
  339. int cntChild = 0;
  340. for(int j=0;j<=jCur;j++)
  341. {
  342. DefaultMutableTreeNode aChild = (DefaultMutableTreeNode)parent.getChildAt(j);
  343. XmlTag aTag = (XmlTag)aChild.getUserObject();
  344. if(lname.equals(aTag.getLocalName())
  345. && ns.equals(aTag.namespace))
  346. {
  347. cntChild++;
  348. }
  349. }
  350. preXPath[i-2] = new Name(ns,lname);
  351. preXPathIndexes[i-2] = cntChild;
  352. /* implementation choice here :
  353. I think the XPath will be more usable
  354. if the same prefix is re-used, even if it's
  355. not the one in the document.
  356. */
  357. if(!nsToPrefix.containsKey(ns))
  358. {
  359. // same prefix, other ns
  360. if( prefixToNS.containsKey(prefix)
  361. && !prefixToNS.get(prefix).equals(ns))
  362. {
  363. /* keep the prefix close
  364. to what was in the document
  365. (only a suffixed number)
  366. */
  367. int uniq=0;
  368. // special case for default
  369. // prefix, since a prefix must
  370. // begin with a letter
  371. if("".equals(prefix))
  372. {
  373. prefix.append(' ');
  374. }
  375. while(prefixToNS.containsKey(prefix.toString() + uniq))
  376. {
  377. uniq++;
  378. }
  379. prefix.append(uniq);
  380. }
  381. prefixToNS.put(prefix.toString(), ns);
  382. nsToPrefix.put(ns, prefix.toString());
  383. }
  384. parent = cur;
  385. }
  386. prefix = new StringBuilder(nsToPrefix.get(rootName.getNamespaceUri()));
  387. xpath.append('/') ;
  388. if(prefix.length() > 0)
  389. xpath.append(prefix).append(':');
  390. xpath.append(rootName.getLocalName());
  391. for(int i=0;i<preXPath.length;i++)
  392. {
  393. prefix = new StringBuilder(nsToPrefix.get(preXPath[i].getNamespaceUri()));
  394. xpath.append('/');
  395. if(prefix.length() > 0)
  396. xpath.append(prefix).append(':');
  397. xpath.append(preXPath[i].getLocalName());
  398. xpath.append('[').append(preXPathIndexes[i]).append(']');
  399. }
  400. }
  401. return xpath == null ? null : xpath.toString();
  402. }
  403. //}}}
  404. //{{{ getAllowedElements() method
  405. /** @return a list containing Elements or Attributes */
  406. public List<ElementDecl> getAllowedElements(Buffer buffer, int pos)
  407. {
  408. /*{{{ debug only */
  409. IAsset asset = getAssetAtOffset(pos);
  410. if(Debug.DEBUG_COMPLETION && asset != null)Log.log(Log.DEBUG, XmlParsedData.class,
  411. ("asset at "+pos+" is :"+asset+" ("+asset.getStart().getOffset()+","+asset.getEnd().getOffset()+")"));
  412. /* }}} */
  413. List<ElementDecl> returnValue = new LinkedList<ElementDecl>();
  414. TagParser.Tag parentTag = null;
  415. try
  416. {
  417. parentTag = TagParser.findLastOpenTag(buffer.getText(0,pos),pos,this);
  418. }
  419. catch (Exception e) {}
  420. if(Debug.DEBUG_COMPLETION)Log.log(Log.DEBUG,XmlParsedData.class,
  421. "parentTag="+parentTag);
  422. if(parentTag == null)
  423. {
  424. // add everything
  425. for(CompletionInfo info: mappings.values()) {
  426. info.getAllElements(returnValue);
  427. }
  428. }
  429. else
  430. {
  431. ElementDecl parentDecl = null;
  432. parentDecl = getElementDecl(parentTag.tag,parentTag.start+1);
  433. if(parentDecl != null){
  434. returnValue.addAll(parentDecl.getChildElements());
  435. }
  436. }
  437. Collections.sort(returnValue, new ElementDecl.Compare());
  438. return returnValue;
  439. } //}}}
  440. //{{{ getAllowedElements() method
  441. /** get allowed elements at startPos or endPos.
  442. * called by updateTagList only.
  443. * ensures:
  444. * - that startPos and endPos are not inside a tag (then returns all global elements),
  445. * - that both startPos and endPos have a parent (may be different) or none has
  446. * (then return startPos parent declaration's children).
  447. * DO KEEP in sync with the other getAllowedElements() !!
  448. * @see XmlParsedData#getAllowedElements(Buffer, int)
  449. */
  450. public List<ElementDecl> getAllowedElements(Buffer buffer, int startPos, int endPos)
  451. {
  452. ArrayList<ElementDecl> returnValue = new ArrayList<ElementDecl>();
  453. CharSequence bufferText = buffer.getSegment(0, buffer.getLength());
  454. // make sure we are not inside a tag
  455. if(TagParser.isInsideTag(bufferText,startPos)) {
  456. return returnValue;
  457. }
  458. // make sure we are not inside a tag
  459. if(TagParser.isInsideTag(bufferText,endPos)) {
  460. return returnValue;
  461. }
  462. TagParser.Tag startParentTag = TagParser.findLastOpenTag(
  463. bufferText,startPos,this);
  464. TagParser.Tag endParentTag = TagParser.findLastOpenTag(
  465. bufferText,endPos,this);
  466. if(startParentTag == null) {
  467. if(endParentTag == null) {
  468. // add everything
  469. for(CompletionInfo info: mappings.values()) {
  470. info.getAllElements(returnValue);
  471. }
  472. }
  473. else
  474. return returnValue;
  475. }
  476. else if(endParentTag == null) {
  477. return returnValue;
  478. }
  479. else
  480. {
  481. ElementDecl startParentDecl = getElementDecl(startParentTag.tag,startParentTag.start+1);
  482. ElementDecl endParentDecl = getElementDecl(endParentTag.tag,endParentTag.start+1);
  483. if(startParentDecl == null)
  484. return returnValue;
  485. else if(endParentDecl == null)
  486. return returnValue;
  487. else
  488. {
  489. returnValue.addAll(startParentDecl.getChildElements());
  490. // only return elements allowed both at startPos and endPos
  491. returnValue.retainAll(endParentDecl.getChildElements());
  492. }
  493. }
  494. Collections.sort(returnValue,new ElementDecl.Compare());
  495. return returnValue;
  496. } //}}}
  497. //{{{ getNamespaceBindings() method
  498. /** namespace to prefix
  499. * (from sidekick)
  500. **/
  501. public Map<String,String> getNamespaceBindings(int pos){
  502. if(html) return new HashMap<String,String>();
  503. if(allNamespacesBindingsAtTop){
  504. return getRootNamespaceBindings();
  505. }else{
  506. TreePath path = getTreePathForPosition(pos);
  507. if(path == null)return null;
  508. else return getNamespaceBindings(path);
  509. }
  510. }
  511. /** namespace to prefix */
  512. private Map<String,String> getNamespaceBindings(TreePath path){
  513. Map<String,String> bindings = new HashMap<String,String>();
  514. if(path == null)return bindings;
  515. if(html)return bindings;
  516. Object[] pathObjs = ((DefaultMutableTreeNode)path.getLastPathComponent()).getUserObjectPath();
  517. for(int i=1;i<pathObjs.length;i++){ // first step is the name of the file
  518. XmlTag t = (XmlTag)pathObjs[i];
  519. if(t.namespaceBindings != null){
  520. bindings.putAll(t.namespaceBindings);
  521. }
  522. }
  523. return bindings;
  524. }
  525. //}}}
  526. //{{{ getRootNamespaceBindings() method
  527. private Map<String,String> getRootNamespaceBindings(){
  528. Map<String,String> bindings;
  529. if(!html && root != null && root.getChildCount() > 0){
  530. TreeNode node = root.getChildAt(0);
  531. DefaultMutableTreeNode dmtn = (DefaultMutableTreeNode)node;
  532. Object o = dmtn.getUserObject();
  533. if(o instanceof XmlTag){
  534. bindings = ((XmlTag)o).namespaceBindings;
  535. }else {
  536. bindings = Collections.emptyMap();
  537. }
  538. }else {
  539. bindings = Collections.emptyMap();
  540. }
  541. if(Debug.DEBUG_COMPLETION)Log.log(Log.DEBUG,XmlParsedData.class,
  542. "getRootNamespaceBindings()=>"+bindings);
  543. return bindings;
  544. }
  545. //}}}
  546. //{{{ getNamespaceForPrefix() method
  547. private String getNamespaceForPrefix(String prefix, int pos){
  548. if(html)return null;
  549. if(allNamespacesBindingsAtTop){
  550. Map<String,String> bindings=getRootNamespaceBindings();
  551. if(bindings != null){
  552. String ns = getNS(bindings,prefix);
  553. if(ns != null)return ns;
  554. }
  555. }else{
  556. TreePath path = getTreePathForPosition(pos);
  557. if(path == null)return null;
  558. Object[] pathObjs = ((DefaultMutableTreeNode)path.getLastPathComponent()).getUserObjectPath();
  559. for(int i=pathObjs.length-1;i>0;i--){ //first object (i==0) is a SourceAsset for the file
  560. XmlTag t = (XmlTag)pathObjs[i];
  561. if(t.namespaceBindings != null){
  562. for(Map.Entry<String,String> binding: t.namespaceBindings.entrySet()){
  563. if(binding.getValue().equals(prefix))return binding.getKey();
  564. }
  565. }
  566. }
  567. }
  568. // no namespace is "" in the mappings
  569. if("".equals(prefix))return "";
  570. else return null;
  571. }
  572. //}}}
  573. //{{{ getNS() method
  574. private String getNS(Map<String,String> nsToPrefix, String prefix){
  575. for(Map.Entry<String,String> en:nsToPrefix.entrySet()){
  576. if(prefix.equals(en.getValue())){
  577. return en.getKey();
  578. }
  579. }
  580. return "";
  581. }
  582. //}}}
  583. //{{{ done() method
  584. /**
  585. * Causes node sorting to be done. Subclasse can override for their own
  586. * purposes, for example, the TldXmlParsedData class renames nodes based
  587. * on child nodes.
  588. */
  589. public void done(View view) {
  590. sort(view);
  591. }
  592. //}}}
  593. //{{{ setSortBy(int) method
  594. public void setSortBy(int by) {
  595. switch (by) {
  596. case SORT_BY_NAME:
  597. case SORT_BY_LINE:
  598. case SORT_BY_TYPE:
  599. sortBy = by;
  600. break;
  601. }
  602. }
  603. //}}}
  604. //{{{ getSortBy() method
  605. public int getSortBy() {
  606. return sortBy;
  607. }
  608. //}}}
  609. //{{{ setSortDirection(boolean) method
  610. public void setSortDirection(boolean down) {
  611. sortDown = down;
  612. }
  613. //}}}
  614. //{{{ sort(view) method
  615. public void sort(final View view) {
  616. SwingUtilities.invokeLater(new Runnable() {
  617. public void run() {
  618. sortChildren((DefaultMutableTreeNode)root);
  619. tree.reload();
  620. expansionModel = createExpansionModel().getModel();
  621. // commented out because it triggers infinite reparsing
  622. //EditBus.send(new SideKickUpdate(view));
  623. }
  624. } );
  625. }
  626. //}}}
  627. //{{{ sortChildren(node) method
  628. private void sortChildren(DefaultMutableTreeNode node) {
  629. List<DefaultMutableTreeNode> children = new ArrayList<DefaultMutableTreeNode>();
  630. Enumeration en = node.children();
  631. while(en.hasMoreElements()) {
  632. children.add((DefaultMutableTreeNode)en.nextElement());
  633. }
  634. Collections.sort(children, getSorter());
  635. node.removeAllChildren();
  636. for (DefaultMutableTreeNode child : children) {
  637. node.add(child);
  638. sortChildren(child);
  639. }
  640. }
  641. //}}}
  642. //{{{ getSorter() method
  643. protected Comparator<DefaultMutableTreeNode> getSorter() {
  644. return new Comparator<DefaultMutableTreeNode>() {
  645. public int compare(DefaultMutableTreeNode tna, DefaultMutableTreeNode tnb) {
  646. int sortBy = getSortBy();
  647. switch (sortBy) { // NOPMD, no breaks are necessary here
  648. case SORT_BY_LINE:
  649. Integer my_line = new Integer(((XmlTag)tna.getUserObject()).getStart().getOffset());
  650. Integer other_line = new Integer(((XmlTag)tnb.getUserObject()).getStart().getOffset());
  651. return my_line.compareTo(other_line) * (sortDown ? 1 : -1);
  652. case SORT_BY_TYPE:
  653. String my_on = ((XmlTag)tna.getUserObject()).getName();
  654. String other_on = ((XmlTag)tnb.getUserObject()).getName();
  655. int comp = my_on.compareTo(other_on) * (sortDown ? 1 : -1);
  656. return comp == 0 ? compareNames(tna, tnb) : comp;
  657. case SORT_BY_NAME:
  658. default:
  659. return compareNames(tna, tnb);
  660. }
  661. }
  662. private int compareNames(DefaultMutableTreeNode tna, DefaultMutableTreeNode tnb) {
  663. // sort by name
  664. String my_name = ((XmlTag)tna.getUserObject()).getLongString();
  665. String other_name = ((XmlTag)tnb.getUserObject()).getLongString();
  666. return my_name.compareTo(other_name) * (sortDown ? 1 : -1);
  667. }
  668. } ;
  669. }//}}}
  670. //{{{ createExpansionModel() method
  671. protected ExpansionModel createExpansionModel() {
  672. ExpansionModel em = new ExpansionModel();
  673. em.add(); // root (filename node)
  674. em.add(); // document node
  675. if (root.getChildCount() != 0) {
  676. for (int i = 0; i < root.getChildAt(0).getChildCount(); i++) {
  677. em.inc(); // first level children. Is this enough?
  678. }
  679. }
  680. return em;
  681. }
  682. //}}}
  683. //{{{ addEntity() method
  684. public void addEntity(EntityDecl entity)
  685. {
  686. entities.add(entity);
  687. if(entity.type == EntityDecl.INTERNAL
  688. && entity.value.length() == 1)
  689. {
  690. Character ch = new Character(entity.value.charAt(0));
  691. entityHash.put(entity.name, ch);
  692. entityHash.put(ch, entity.name);
  693. }
  694. } //}}}
  695. //{{{ getObjectsTo(pos) method
  696. public Object[] getObjectsTo(int pos){
  697. TreePath path = getTreePathForPosition(pos);
  698. if(path == null)return null;
  699. Object[] pathObjs = ((DefaultMutableTreeNode)path.getLastPathComponent()).getUserObjectPath();
  700. return pathObjs;
  701. }//}}}
  702. //{{{ getIDDecl(id) method
  703. /**
  704. * convenience method to find an IDDecl by name
  705. * @return found IDDecl or null
  706. */
  707. public IDDecl getIDDecl(String id){
  708. return ids.get(id);
  709. }//}}}
  710. //{{{ getSortedIds() method
  711. public List<IDDecl> getSortedIds(){
  712. List<IDDecl> idl = new ArrayList<IDDecl>(ids.values());
  713. return idl;
  714. }//}}}
  715. //{{{ getParsedData() method
  716. /**
  717. * get parsed data as XmlParsedData.
  718. * @param view current view
  719. * @param signalError shows an error dialog when not an XmlParsedData
  720. * @return parsed data or null
  721. */
  722. public static XmlParsedData getParsedData(View view, boolean signalError)
  723. {
  724. SideKickParsedData _data = SideKickParsedData.getParsedData(view);
  725. if(_data==null || !(_data instanceof XmlParsedData))
  726. {
  727. if(signalError)
  728. {
  729. GUIUtilities.error(view,"xml-no-data",null);
  730. }
  731. return null;
  732. }
  733. return (XmlParsedData)_data;
  734. }//}}}
  735. //{{{ getElementNamePrefix() method
  736. public static String getElementNamePrefix(String name)
  737. {
  738. int index = name.indexOf(':');
  739. if(index == -1)
  740. return "";
  741. else
  742. return name.substring(0,index);
  743. } //}}}
  744. //{{{ getElementLocalName() method
  745. public static String getElementLocalName(String name)
  746. {
  747. int index = name.indexOf(':');
  748. if(index == -1)
  749. return name;
  750. else
  751. return name.substring(index+1);
  752. } //}}}
  753. }