PageRenderTime 39ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/bundles/plugins-trunk/XML/xml/parser/XSDSchemaToCompletion.java

#
Java | 424 lines | 270 code | 51 blank | 103 comment | 73 complexity | 6237edd0b2c9ba906fc79be87a408b3e 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. * XSDSchemaToCompletion.java
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 2010-2011 Eric Le Lay
  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.parser;
  16. // {{{ imports
  17. import java.util.List;
  18. import java.util.ArrayList;
  19. import java.util.Map;
  20. import java.util.HashMap;
  21. import java.util.HashSet;
  22. import java.util.Collections;
  23. import java.io.IOException;
  24. import javax.xml.XMLConstants;
  25. import org.xml.sax.ErrorHandler;
  26. import org.xml.sax.SAXException;
  27. import org.apache.xerces.impl.xs.XSParticleDecl;
  28. import org.apache.xerces.xs.StringList;
  29. import org.apache.xerces.xs.XSAttributeDeclaration;
  30. import org.apache.xerces.xs.XSAttributeUse;
  31. import org.apache.xerces.xs.XSComplexTypeDefinition;
  32. import org.apache.xerces.xs.XSConstants;
  33. import org.apache.xerces.xs.XSElementDeclaration;
  34. import org.apache.xerces.xs.XSModel;
  35. import org.apache.xerces.xs.XSModelGroup;
  36. import org.apache.xerces.xs.XSNamedMap;
  37. import org.apache.xerces.xs.XSObjectList;
  38. import org.apache.xerces.xs.XSParticle;
  39. import org.apache.xerces.xs.XSSimpleTypeDefinition;
  40. import org.apache.xerces.xs.XSTerm;
  41. import org.apache.xerces.xs.XSTypeDefinition;
  42. import org.apache.xerces.xs.XSWildcard;
  43. import org.apache.xerces.xs.XSNamespaceItem;
  44. import org.apache.xerces.xs.XSNamespaceItemList;
  45. import org.gjt.sp.util.Log;
  46. import org.gjt.sp.jedit.Buffer;
  47. import xml.completion.CompletionInfo;
  48. import xml.completion.ElementDecl;
  49. import static xml.Debug.*;
  50. import xml.Resolver;
  51. import xml.cache.Cache;
  52. import xml.cache.CacheEntry;
  53. // }}}
  54. // {{{ class XSDSchemaToCompletion
  55. /**
  56. * turns a Xerces XSD object model into CompletionInfo
  57. *
  58. * @author kerik-sf
  59. * @version $Id: XSDSchemaToCompletion.java 21323 2012-03-11 10:35:52Z kerik-sf $
  60. */
  61. public class XSDSchemaToCompletion{
  62. //{{{ xsElementToElementDecl() method
  63. private static void xsElementToElementDecl(XSNamedMap elements, Map<String,CompletionInfo> infos,
  64. XSElementDeclaration element, ElementDecl parent, Map<XSComplexTypeDefinition, List<ElementDecl>> seenComplexTypes)
  65. {
  66. if(parent != null && parent.content == null)
  67. parent.content = new HashSet<String>();
  68. if(parent != null && parent.elementHash == null)
  69. parent.elementHash = new HashMap<String,ElementDecl>();
  70. String name = element.getName();
  71. String namespace = element.getNamespace();
  72. if(namespace == null)namespace = "";
  73. if(DEBUG_XSD_SCHEMA)Log.log(Log.DEBUG,XSDSchemaToCompletion.class,"xsElementToElementDecl("+namespace+":"+name+")");
  74. CompletionInfo info;
  75. if(infos.containsKey(namespace)){
  76. info = infos.get(namespace);
  77. }else{
  78. info = new CompletionInfo();
  79. info.namespace = namespace;
  80. infos.put(namespace, info);
  81. }
  82. if(info.elementHash.containsKey(name))
  83. {
  84. // one must add the element to its parent's content, even if
  85. // one knows the element already
  86. if(parent!=null)
  87. {
  88. parent.content.add(name);
  89. parent.elementHash.put(name,info.elementHash.get(name));
  90. }
  91. return;
  92. }
  93. ElementDecl elementDecl = null;
  94. if ( element.getAbstract()
  95. /* I don't understand this condition.
  96. As far as I understand, every top level element can be
  97. head of a substitution group.
  98. An algorithm showing quadratic performance :
  99. for each element e as argument to this method,
  100. for each element f in elements
  101. verify the substitution group of f and if e is the head, add f to parent
  102. TODO: write an example, fix the code
  103. || element.getName().endsWith(".class") */
  104. )
  105. {
  106. if( parent != null ) {
  107. for (int j=0; j<elements.getLength(); ++j) {
  108. XSElementDeclaration decl = (XSElementDeclaration)elements.item(j);
  109. XSElementDeclaration group = decl.getSubstitutionGroupAffiliation();
  110. if (group != null && group.getName().equals(name)) {
  111. // allows to handle elements which are themselves abstract
  112. // see otherComment in abstract_substitution/comments.xsd
  113. xsElementToElementDecl(elements, infos, decl, parent, seenComplexTypes);
  114. }
  115. }
  116. }
  117. /* we shouldn't care about the type of an abstract element,
  118. as it's not allowed in a document. Would it be the case,
  119. one should not forget to fix the NullPointerException on elementDecl
  120. that will arise when setting the attributes. Maybe use the type declaration
  121. for every element...*/
  122. return;
  123. }
  124. else {
  125. elementDecl = new ElementDecl(info, name, null);
  126. /* prevent infinite recursion caused by complex types referencing themselves
  127. following code is awkward :
  128. - if element is of complex type and this complex type is being
  129. expandend (ie is in seenComplexTypes)
  130. - if it's used with the same name:
  131. - reuse the ElementDecl,
  132. - add the ElementDecl to parent
  133. - return
  134. - if it's used with a different name
  135. - copy its content into the new ElementDecl
  136. - add the new ElementDecl to parent and CompletionInfo as
  137. in the normal case
  138. - don't expand the complextype, since it's been already seen
  139. - otherwise add to parent and CompletionInfo, expand
  140. */
  141. XSTypeDefinition typedef = element.getTypeDefinition();
  142. XSComplexTypeDefinition complex = null;
  143. boolean needToCalcContent = true;
  144. // {{{ prevent infinite recursion caused by complex types referencing themselves
  145. if(typedef.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE)
  146. {
  147. complex = (XSComplexTypeDefinition)typedef;
  148. if(seenComplexTypes.containsKey(complex)){
  149. ElementDecl same = null;
  150. for(ElementDecl decl:seenComplexTypes.get(complex)){
  151. if(decl.completionInfo == info
  152. && decl.name.equals(name))
  153. {
  154. same = decl;
  155. break;
  156. }
  157. }
  158. if(same == null){
  159. // same complextype but different names
  160. ElementDecl sameContent = seenComplexTypes.get(complex).get(0);
  161. elementDecl.attributes = sameContent.attributes;
  162. elementDecl.attributeHash = sameContent.attributeHash;
  163. elementDecl.content = sameContent.content;
  164. elementDecl.elementHash = sameContent.elementHash;
  165. // don't need to expand, but need to be add it to parent and CompletionInfo
  166. needToCalcContent = false;
  167. } else {
  168. // really same element
  169. if (parent != null) {
  170. parent.elementHash.put(name,same);
  171. parent.content.add(name);
  172. }
  173. // it's already in CompletionInfo
  174. return;
  175. }
  176. }
  177. }// }}}
  178. // {{{ add new ElementDecl to parent and CompletionInfo
  179. // don't let locally defined elements take precedence other global elements
  180. // see test_data/multiple_name
  181. if(element.getScope() == XSConstants.SCOPE_LOCAL)
  182. { // FIXME: why not use a second elementHash (containing local elements) instead of looping through elements ?
  183. // yes, it's another data structure, but it would speed this up !
  184. if(!info.nameConflict){
  185. for(ElementDecl e:info.elements){
  186. if(e.name.equals(name)){
  187. info.nameConflict = true;
  188. //Log.log(Log.DEBUG,XSDSchemaToCompletion.class,
  189. // "conflict in "+namespace+" between 2 "+name);
  190. }
  191. }
  192. }
  193. info.elements.add(elementDecl);
  194. }
  195. else
  196. {
  197. info.addElement(elementDecl);
  198. }
  199. if (parent != null) {
  200. parent.elementHash.put(name,elementDecl);
  201. parent.content.add(name);
  202. }
  203. //}}}
  204. // {{{ Expand complex type
  205. if(complex != null && needToCalcContent)
  206. {
  207. // push seen complex type
  208. List<ElementDecl> l = new ArrayList<ElementDecl>();
  209. l.add(elementDecl);
  210. seenComplexTypes.put(complex,l);
  211. XSParticle particle = complex.getParticle();
  212. if(particle != null)
  213. {
  214. XSTerm particleTerm = particle.getTerm();
  215. if(particleTerm instanceof XSWildcard)
  216. elementDecl.any = true;
  217. else
  218. xsTermToElementDecl(elements, infos,particleTerm,elementDecl,seenComplexTypes);
  219. }
  220. // FIXME: not removing from seenComplexTypes gives a smaller CompletionInfo
  221. // but is it correct ?
  222. XSObjectList attributes = complex.getAttributeUses();
  223. for(int i = 0; i < attributes.getLength(); i++)
  224. {
  225. XSAttributeUse attr = (XSAttributeUse)
  226. attributes.item(i);
  227. xsAttributeToElementDecl(elementDecl,attr.getAttrDeclaration(),attr.getRequired());
  228. }
  229. } //}}}
  230. }
  231. } //}}}
  232. private static void xsAttributeToElementDecl(ElementDecl elementDecl,XSAttributeDeclaration decl, boolean required){
  233. String attrName = decl.getName();
  234. String attrNamespace = decl.getNamespace();
  235. String value = decl.getConstraintValue();
  236. XSSimpleTypeDefinition typeDef = decl.getTypeDefinition();
  237. String type = typeDef.getName();
  238. StringList valueStringList = typeDef.getLexicalEnumeration();
  239. ArrayList<String> values = new ArrayList<String>();
  240. for (int j = 0; j < valueStringList.getLength(); j++) {
  241. values.add(valueStringList.item(j));
  242. }
  243. if(type == null)
  244. type = "CDATA";
  245. elementDecl.addAttribute(new ElementDecl.AttributeDecl(
  246. attrName,attrNamespace,value,values,type,required));
  247. }
  248. //{{{ xsTermToElementDecl() method
  249. private static void xsTermToElementDecl(XSNamedMap elements, Map<String,CompletionInfo> infos, XSTerm term,
  250. ElementDecl parent, Map<XSComplexTypeDefinition, List<ElementDecl>> seenComplexTypes)
  251. {
  252. if(term instanceof XSElementDeclaration)
  253. {
  254. xsElementToElementDecl(elements, infos,
  255. (XSElementDeclaration)term, parent, seenComplexTypes);
  256. }
  257. else if(term instanceof XSModelGroup)
  258. {
  259. XSObjectList content = ((XSModelGroup)term).getParticles();
  260. for(int i = 0; i < content.getLength(); i++)
  261. {
  262. XSTerm childTerm = ((XSParticleDecl)content.item(i)).getTerm();
  263. xsTermToElementDecl(elements, infos,childTerm,parent, seenComplexTypes);
  264. }
  265. }
  266. }
  267. //}}}
  268. //{{{ modelToCompletionInfo() method
  269. public static Map<String,CompletionInfo> modelToCompletionInfo(XSModel model)
  270. {
  271. if(DEBUG_XSD_SCHEMA)Log.log(Log.DEBUG,XSDSchemaToCompletion.class,"modelToCompletionInfo("+model+")");
  272. Map<String,CompletionInfo> infos = new HashMap<String,CompletionInfo>();
  273. XSNamedMap elements = model.getComponents(XSConstants.ELEMENT_DECLARATION);
  274. Map<XSComplexTypeDefinition, List<ElementDecl>> seenComplexTypes = new HashMap<XSComplexTypeDefinition, List<ElementDecl>>();
  275. for(int i = 0; i < elements.getLength(); i++)
  276. {
  277. XSElementDeclaration element = (XSElementDeclaration)
  278. elements.item(i);
  279. xsElementToElementDecl(elements, infos, element, null, seenComplexTypes);
  280. }
  281. /* // don't need them : they are declared for each element
  282. // tested with xml:base in user-guide.xml
  283. /*XSNamedMap attributes = model.getComponents(XSConstants.ATTRIBUTE_DECLARATION);
  284. for(int i = 0; i < attributes.getLength(); i++)
  285. {
  286. XSAttributeDeclaration attribute = (XSAttributeDeclaration)attributes.item(i);
  287. //indeed, it's possible (like for XMLSchema-instance),
  288. //when one uses getModelForNamespace("http://www.w3.org/2001/XMLSchema-instance")
  289. //or http://www.w3.org/XML/1998/namespace : see network.xml : base, lang, space
  290. System.err.println("look! " + attribute.getName());
  291. for(CompletionInfo info : infos.values()){
  292. for(ElementDecl e:info.elements){
  293. xsAttributeToElementDecl(e,attribute,false);
  294. }
  295. }
  296. }*/
  297. return infos;
  298. } //}}}
  299. // {{{ getCompletionInfoFromSchema() method
  300. /**
  301. * parse a schema and return CompletionInfos for all its target namespaces
  302. * @param location location of the schema to parse
  303. * @param schemaLocation namespace-location pairs found in xsi:schemaLocation attribute. Used to resolve imported schema
  304. * @param nonsSchemaLocation location found in xsi:noNamespaceSchemaLocation attribute. Used to resolve imported schema
  305. * @param errorHandler to report errors while parsing the schema
  306. * @param buffer requesting buffer, for caching
  307. */
  308. public static Map<String,CompletionInfo> getCompletionInfoFromSchema(String location, String schemaLocation, String nonsSchemaLocation, ErrorHandler errorHandler, Buffer buffer)
  309. throws IOException, SAXException{
  310. if(DEBUG_XSD_SCHEMA)Log.log(Log.DEBUG,XSDSchemaToCompletion.class,"getCompletionInfoFromSchema("+location+","+schemaLocation+","+nonsSchemaLocation+","+buffer.getPath()+")");
  311. String realLocation = Resolver.instance().resolveEntityToPath(
  312. null,//name
  313. null,//public Id
  314. buffer.getPath(),//current
  315. location// systemId
  316. );
  317. if(realLocation==null){
  318. // resolved location really shouldn't be null
  319. throw new IOException("unable to resolve grammar location for : "+location);
  320. }else{
  321. CacheEntry entry = Cache.instance().get(realLocation,XercesParserImpl.COMPLETION_INFO_CACHE_ENTRY);
  322. if(entry == null){
  323. entry = Cache.instance().get(realLocation,XMLConstants.W3C_XML_SCHEMA_NS_URI);
  324. org.apache.xerces.xni.grammars.Grammar grammar = null;
  325. if(entry == null){
  326. if(DEBUG_CACHE)Log.log(Log.DEBUG,XSDSchemaToCompletion.class,"no Grammar from cache for "+location);
  327. grammar = SchemaLoader.instance().loadXercesGrammar(buffer, location, schemaLocation, nonsSchemaLocation, errorHandler);
  328. }else{
  329. if(DEBUG_CACHE)Log.log(Log.DEBUG,XSDSchemaToCompletion.class,"got Grammar from cache for "+location);
  330. grammar = (org.apache.xerces.xni.grammars.Grammar)entry.getCachedItem();
  331. }
  332. if(grammar == null){
  333. throw new SAXException("couldn't load grammar "+location+" from "+realLocation);
  334. }else{
  335. XSModel model = ((org.apache.xerces.xni.grammars.XSGrammar)grammar).toXSModel();
  336. XSNamespaceItemList namespaces = model.getNamespaceItems();
  337. List<CacheEntry> related = new ArrayList<CacheEntry>(namespaces.getLength());
  338. for(int i=0;i<namespaces.getLength();i++){
  339. XSNamespaceItem namespace = namespaces.item(i);
  340. if(DEBUG_XSD_SCHEMA)Log.log(Log.DEBUG,XSDSchemaToCompletion.class,"grammar is composed of "
  341. +namespace.getSchemaNamespace());
  342. StringList l = namespace.getDocumentLocations();
  343. for(int j=0;j<l.getLength();j++){
  344. String loc = l.item(j);
  345. if(DEBUG_XSD_SCHEMA)Log.log(Log.DEBUG,XSDSchemaToCompletion.class," @"+loc);
  346. try{
  347. String realLoc = Resolver.instance().resolveEntityToPath(null, null, buffer.getPath(), l.item(j));
  348. CacheEntry ce = Cache.instance().put(realLoc,"GrammarComponent","Dummy");
  349. related.add(ce);
  350. }catch(IOException ioe){
  351. Log.log(Log.ERROR, XSDSchemaToCompletion.class, "error resolving path for "+loc, ioe);
  352. }
  353. }
  354. }
  355. Map<String,CompletionInfo> infos = XSDSchemaToCompletion.modelToCompletionInfo(model);
  356. related.add(Cache.instance().put(realLocation,XercesParserImpl.COMPLETION_INFO_CACHE_ENTRY,infos));
  357. // mark all components related and requested by the buffer
  358. for(CacheEntry ce : related){
  359. ce.getRelated().addAll(related);
  360. ce.getRelated().remove(ce);
  361. ce.getRequestingBuffers().add(buffer);
  362. }
  363. return infos;
  364. }
  365. }else{
  366. if(DEBUG_CACHE)Log.log(Log.DEBUG,XSDSchemaToCompletion.class,"got CompletionInfo from cache for "+location);
  367. entry.addRequestingBuffer(buffer);
  368. Map<String,CompletionInfo> infos = (Map<String,CompletionInfo>)entry.getCachedItem();
  369. return infos;
  370. }
  371. }
  372. } //}}}
  373. }