/plugins/XML/tags/release-2-5/xml/XmlParsedData.java

# · Java · 380 lines · 270 code · 50 blank · 60 comment · 52 complexity · 5755ee2e3f7d7d5e62b3f22affe86ed4 MD5 · raw file

  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.HashMap;
  20. import java.util.Iterator;
  21. import java.util.LinkedList;
  22. import java.util.List;
  23. import java.util.Map;
  24. import javax.swing.tree.DefaultTreeModel;
  25. import javax.swing.tree.DefaultMutableTreeNode;
  26. import javax.swing.tree.TreePath;
  27. import javax.swing.tree.TreeNode;
  28. import org.gjt.sp.jedit.Buffer;
  29. import org.gjt.sp.util.StringList;
  30. import sidekick.SideKickParsedData;
  31. import xml.completion.CompletionInfo;
  32. import xml.completion.ElementDecl;
  33. import xml.completion.EntityDecl;
  34. import xml.completion.IDDecl;
  35. import xml.parser.TagParser;
  36. import xml.parser.XmlTag;
  37. import com.thaiopensource.xml.util.Name;
  38. //}}}
  39. /**
  40. * Encapsulates the results of parsing a buffer, either using Xerces or the
  41. * Swing HTML parser.
  42. */
  43. public class XmlParsedData extends SideKickParsedData
  44. {
  45. public boolean html;
  46. /**
  47. * A mapping of namespace to CompletionInfo objects.
  48. * namespace of "" is the default namespace.
  49. */
  50. private Map<String, CompletionInfo> mappings;
  51. /**
  52. * A list of all identifiers encountered during the parse?
  53. */
  54. public List<IDDecl> ids;
  55. public void setCompletionInfo(String namespace, CompletionInfo info) {
  56. mappings.put(namespace, info);
  57. }
  58. //{{{ XmlParsedData constructor
  59. public XmlParsedData(String fileName, boolean html)
  60. {
  61. super(fileName);
  62. this.html = html;
  63. mappings = new HashMap<String, CompletionInfo>();
  64. ids = new ArrayList<IDDecl>();
  65. } //}}}
  66. //{{{ getNoNamespaceCompletionInfo() method
  67. public CompletionInfo getNoNamespaceCompletionInfo()
  68. {
  69. CompletionInfo info = mappings.get("");
  70. if(info == null)
  71. {
  72. info = new CompletionInfo();
  73. mappings.put("",info);
  74. }
  75. return info;
  76. } //}}}
  77. //{{{ getElementDecl() method
  78. public ElementDecl getElementDecl(String name)
  79. {
  80. if(html)
  81. name = name.toLowerCase();
  82. String prefix = getElementNamePrefix(name);
  83. CompletionInfo info = mappings.get(prefix);
  84. if(info == null)
  85. return null;
  86. else
  87. {
  88. String lName;
  89. int prefixLen = prefix.length();
  90. if(prefixLen == 0)
  91. lName = name;
  92. else
  93. lName = name.substring(prefixLen + 1);
  94. ElementDecl decl = info.elementHash.get(lName);
  95. if(decl == null)
  96. return null;
  97. else
  98. return decl.withPrefix(prefix);
  99. }
  100. } //}}}
  101. //{{{ getXPathForPosition() method
  102. public String getXPathForPosition(int pos)
  103. {
  104. TreePath path = getTreePathForPosition(pos);
  105. if(path == null)return null;
  106. DefaultMutableTreeNode tn = (DefaultMutableTreeNode)path.getLastPathComponent();
  107. TreeNode[]steps = tn.getPath();
  108. DefaultMutableTreeNode parent = (DefaultMutableTreeNode)steps[0];
  109. String xpath = "";
  110. if(steps.length == 1)
  111. {
  112. //there is only the node with the file name
  113. xpath = null;
  114. }
  115. else
  116. {
  117. parent = (DefaultMutableTreeNode)steps[1];
  118. if( ! (parent.getUserObject() instanceof XmlTag))
  119. {
  120. // TODO: maybe implement it also for HTML
  121. return null;
  122. }
  123. Map<String,String> prefixToNS = new HashMap<String,String>();
  124. Map<String,String> nsToPrefix = new HashMap<String,String>();
  125. Name[] preXPath = new Name[steps.length-2];
  126. int[] preXPathIndexes = new int[steps.length-2];
  127. Name rootName;
  128. XmlTag curTag = (XmlTag)parent.getUserObject();
  129. String lname = curTag.getLocalName();
  130. String ns = curTag.namespace;
  131. String prefix = curTag.getPrefix();
  132. assert(ns != null);
  133. rootName = new Name(ns,lname);
  134. prefixToNS.put(prefix,ns);
  135. nsToPrefix.put(ns,prefix);
  136. for(int i=2;i<steps.length;i++)
  137. {
  138. DefaultMutableTreeNode cur=(DefaultMutableTreeNode)steps[i];
  139. curTag = (XmlTag)cur.getUserObject();
  140. ns = curTag.namespace;
  141. lname = curTag.getLocalName();
  142. prefix = curTag.getPrefix();
  143. int jCur = parent.getIndex(cur);
  144. int cntChild = 0;
  145. for(int j=0;j<=jCur;j++)
  146. {
  147. DefaultMutableTreeNode aChild = (DefaultMutableTreeNode)parent.getChildAt(j);
  148. XmlTag aTag = (XmlTag)aChild.getUserObject();
  149. if(lname.equals(aTag.getLocalName())
  150. && ns.equals(aTag.namespace))
  151. {
  152. cntChild++;
  153. }
  154. }
  155. preXPath[i-2] = new Name(ns,lname);
  156. preXPathIndexes[i-2] = cntChild;
  157. /* implementation choice here :
  158. I think the XPath will be more usable
  159. if the same prefix is re-used, even if it's
  160. not the one in the document.
  161. */
  162. if(!nsToPrefix.containsKey(ns))
  163. {
  164. // same prefix, other ns
  165. if( prefixToNS.containsKey(prefix)
  166. && !prefixToNS.get(prefix).equals(ns))
  167. {
  168. /* keep the prefix close
  169. to what was in the document
  170. (only a suffixed number)
  171. */
  172. int uniq=0;
  173. // special case for default
  174. // prefix, since a prefix must
  175. // begin with a letter
  176. if("".equals(prefix))
  177. {
  178. prefix+= "_";
  179. }
  180. while(prefixToNS.containsKey(prefix+String.valueOf(uniq)))
  181. {
  182. uniq++;
  183. }
  184. prefix += String.valueOf(uniq);
  185. }
  186. prefixToNS.put(prefix,ns);
  187. nsToPrefix.put(ns,prefix);
  188. }
  189. parent = cur;
  190. }
  191. prefix = nsToPrefix.get(rootName.getNamespaceUri());
  192. xpath = "/" ;
  193. if(!"".equals(prefix)) xpath += prefix + ":";
  194. xpath += rootName.getLocalName();
  195. for(int i=0;i<preXPath.length;i++)
  196. {
  197. prefix = nsToPrefix.get(preXPath[i].getNamespaceUri());
  198. xpath += "/";
  199. if(!"".equals(prefix))xpath+= prefix + ":";
  200. xpath += preXPath[i].getLocalName();
  201. xpath += "[" + preXPathIndexes[i] + "]";
  202. }
  203. }
  204. return xpath;
  205. }
  206. //}}}
  207. //{{{ getAllowedElements() method
  208. /** @returns a list containing Elements or Attributes */
  209. public List<ElementDecl> getAllowedElements(Buffer buffer, int pos)
  210. {
  211. List<ElementDecl> returnValue = new LinkedList<ElementDecl>();
  212. TagParser.Tag parentTag = null;
  213. try {
  214. parentTag = TagParser.findLastOpenTag(buffer.getText(0,pos),pos,this);
  215. }
  216. catch (Exception e) {}
  217. if(parentTag == null)
  218. {
  219. // add everything
  220. Iterator iter = mappings.keySet().iterator();
  221. while(iter.hasNext())
  222. {
  223. String prefix = (String)iter.next();
  224. CompletionInfo info = (CompletionInfo)
  225. mappings.get(prefix);
  226. info.getAllElements(prefix, returnValue);
  227. }
  228. }
  229. else
  230. {
  231. String parentPrefix = getElementNamePrefix(parentTag.tag);
  232. ElementDecl parentDecl = getElementDecl(parentTag.tag);
  233. if(parentDecl != null)
  234. returnValue.addAll(parentDecl.getChildElements(parentPrefix));
  235. // add everything but the parent's prefix now
  236. Iterator iter = mappings.keySet().iterator();
  237. while(iter.hasNext())
  238. {
  239. String prefix = (String)iter.next();
  240. if(!prefix.equals(parentPrefix))
  241. {
  242. CompletionInfo info = (CompletionInfo)
  243. mappings.get(prefix);
  244. info.getAllElements(prefix,returnValue);
  245. }
  246. }
  247. }
  248. Collections.sort(returnValue, new ElementDecl.Compare());
  249. return returnValue;
  250. } //}}}
  251. //{{{ getAllowedElements() method
  252. /* called by updateTagList only */
  253. public List getAllowedElements(Buffer buffer, int startPos, int endPos)
  254. {
  255. ArrayList returnValue = new ArrayList();
  256. // make sure we are not inside a tag
  257. if(TagParser.isInsideTag(buffer.getText(0,startPos),startPos)) {
  258. return returnValue;
  259. }
  260. // make sure we are not inside a tag
  261. if(TagParser.isInsideTag(buffer.getText(0,endPos),endPos)) {
  262. return returnValue;
  263. }
  264. TagParser.Tag startParentTag = TagParser.findLastOpenTag(
  265. buffer.getText(0,startPos),startPos,this);
  266. TagParser.Tag endParentTag = TagParser.findLastOpenTag(
  267. buffer.getText(0,endPos),endPos,this);
  268. if(startParentTag == null) {
  269. if(endParentTag == null) {
  270. // add everything
  271. Iterator iter = mappings.keySet().iterator();
  272. while(iter.hasNext())
  273. {
  274. String prefix = (String)iter.next();
  275. CompletionInfo info = (CompletionInfo)
  276. mappings.get(prefix);
  277. info.getAllElements(prefix,returnValue);
  278. }
  279. }
  280. else
  281. return returnValue;
  282. }
  283. else if(endParentTag == null) {
  284. return returnValue;
  285. }
  286. else
  287. {
  288. String startParentPrefix = getElementNamePrefix(startParentTag.tag);
  289. ElementDecl startParentDecl = getElementDecl(startParentTag.tag);
  290. String endParentPrefix = getElementNamePrefix(endParentTag.tag);
  291. ElementDecl endParentDecl = getElementDecl(endParentTag.tag);
  292. if(startParentDecl == null)
  293. return returnValue;
  294. else if(endParentDecl == null)
  295. return returnValue;
  296. else if(!startParentPrefix.equals(endParentPrefix))
  297. return returnValue;
  298. else
  299. {
  300. if(startParentDecl != null)
  301. returnValue.addAll(startParentDecl.getChildElements(startParentPrefix));
  302. // add everything but the parent's prefix now
  303. Iterator iter = mappings.keySet().iterator();
  304. while(iter.hasNext())
  305. {
  306. String prefix = (String)iter.next();
  307. if(!prefix.equals(startParentPrefix))
  308. {
  309. CompletionInfo info = (CompletionInfo)
  310. mappings.get(prefix);
  311. info.getAllElements(prefix,returnValue);
  312. }
  313. }
  314. }
  315. }
  316. Collections.sort(returnValue,new ElementDecl.Compare());
  317. return returnValue;
  318. } //}}}
  319. //{{{ Private members
  320. //{{{ getElementPrefix() method
  321. private static String getElementNamePrefix(String name)
  322. {
  323. int index = name.indexOf(':');
  324. if(index == -1)
  325. return "";
  326. else
  327. return name.substring(0,index);
  328. } //}}}
  329. //}}}
  330. }