PageRenderTime 57ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/bundles/plugins-trunk/XML/xml/translate/BufferDtdConverter.java

#
Java | 1164 lines | 955 code | 125 blank | 84 comment | 206 complexity | d9030a86396cbd4e7c3725ae8c628f0b 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. Copyright (c) 2001-2003 Thai Open Source Software Center Ltd
  3. All rights reserved.
  4. Redistribution and use in source and binary forms, with or without
  5. modification, are permitted provided that the following conditions are
  6. met:
  7. Redistributions of source code must retain the above copyright
  8. notice, this list of conditions and the following disclaimer.
  9. Redistributions in binary form must reproduce the above copyright
  10. notice, this list of conditions and the following disclaimer in
  11. the documentation and/or other materials provided with the
  12. distribution.
  13. Neither the name of the Thai Open Source Software Center Ltd nor
  14. the names of its contributors may be used to endorse or promote
  15. products derived from this software without specific prior written
  16. permission.
  17. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  18. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19. LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  20. A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
  21. CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  22. EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  23. PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  24. PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  25. LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  26. NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  27. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. */
  29. package xml.translate;
  30. import com.thaiopensource.relaxng.edit.Annotated;
  31. import com.thaiopensource.relaxng.edit.AnyNameNameClass;
  32. import com.thaiopensource.relaxng.edit.AttributeAnnotation;
  33. import com.thaiopensource.relaxng.edit.AttributePattern;
  34. import com.thaiopensource.relaxng.edit.ChoicePattern;
  35. import com.thaiopensource.relaxng.edit.Combine;
  36. import com.thaiopensource.relaxng.edit.Comment;
  37. import com.thaiopensource.relaxng.edit.Component;
  38. import com.thaiopensource.relaxng.edit.CompositePattern;
  39. import com.thaiopensource.relaxng.edit.DataPattern;
  40. import com.thaiopensource.relaxng.edit.DefineComponent;
  41. import com.thaiopensource.relaxng.edit.ElementPattern;
  42. import com.thaiopensource.relaxng.edit.EmptyPattern;
  43. import com.thaiopensource.relaxng.edit.GrammarPattern;
  44. import com.thaiopensource.relaxng.edit.GroupPattern;
  45. import com.thaiopensource.relaxng.edit.IncludeComponent;
  46. import com.thaiopensource.relaxng.edit.NameClass;
  47. import com.thaiopensource.relaxng.edit.NameNameClass;
  48. import com.thaiopensource.relaxng.edit.NotAllowedPattern;
  49. import com.thaiopensource.relaxng.edit.OneOrMorePattern;
  50. import com.thaiopensource.relaxng.edit.OptionalPattern;
  51. import com.thaiopensource.relaxng.edit.Pattern;
  52. import com.thaiopensource.relaxng.edit.RefPattern;
  53. import com.thaiopensource.relaxng.edit.SchemaCollection;
  54. import com.thaiopensource.relaxng.edit.SchemaDocument;
  55. import com.thaiopensource.relaxng.edit.TextPattern;
  56. import com.thaiopensource.relaxng.edit.ValuePattern;
  57. import com.thaiopensource.relaxng.edit.ZeroOrMorePattern;
  58. import com.thaiopensource.relaxng.input.CommentTrimmer;
  59. import com.thaiopensource.relaxng.output.common.ErrorReporter;
  60. import com.thaiopensource.xml.dtd.om.AttributeDefault;
  61. import com.thaiopensource.xml.dtd.om.AttributeGroup;
  62. import com.thaiopensource.xml.dtd.om.AttributeGroupMember;
  63. import com.thaiopensource.xml.dtd.om.AttributeGroupVisitor;
  64. import com.thaiopensource.xml.dtd.om.Datatype;
  65. import com.thaiopensource.xml.dtd.om.DatatypeVisitor;
  66. import com.thaiopensource.xml.dtd.om.Def;
  67. import com.thaiopensource.xml.dtd.om.Dtd;
  68. import com.thaiopensource.xml.dtd.om.EnumGroup;
  69. import com.thaiopensource.xml.dtd.om.EnumGroupVisitor;
  70. import com.thaiopensource.xml.dtd.om.Flag;
  71. import com.thaiopensource.xml.dtd.om.ModelGroup;
  72. import com.thaiopensource.xml.dtd.om.ModelGroupVisitor;
  73. import com.thaiopensource.xml.dtd.om.NameSpec;
  74. import com.thaiopensource.xml.dtd.om.TokenizedDatatype;
  75. import com.thaiopensource.xml.dtd.om.TopLevel;
  76. import com.thaiopensource.xml.dtd.om.TopLevelVisitor;
  77. import com.thaiopensource.xml.em.ExternalId;
  78. import com.thaiopensource.xml.util.WellKnownNamespaces;
  79. import java.util.Collections;
  80. import java.util.HashMap;
  81. import java.util.HashSet;
  82. import java.util.List;
  83. import java.util.Map;
  84. import java.util.Set;
  85. import java.util.Vector;
  86. /* copy of http://jing-trang.googlecode.com/svn/tags/V20091111/mod/convert-from-dtd/src/main/com/thaiopensource/relaxng/input/dtd/Converter.java
  87. * it is necessary since the Options class is package protected and I need to create one in BufferDtdInputFormat
  88. */
  89. public class BufferDtdConverter {
  90. static class Options {
  91. boolean inlineAttlistDecls;
  92. boolean generateStart = true;
  93. boolean strictAny;
  94. String elementDeclPattern;
  95. String attlistDeclPattern;
  96. String colonReplacement;
  97. String anyName;
  98. String annotationPrefix;
  99. String defaultNamespace;
  100. final Map<String, String> prefixMap = new HashMap<String, String>();
  101. }
  102. private final Dtd dtd;
  103. private final ErrorReporter er;
  104. private final SchemaCollection sc = new SchemaCollection();
  105. private final Options options;
  106. /**
  107. * true if any uses of ANY have been encountered in the DTD
  108. */
  109. private boolean hadAny = false;
  110. /**
  111. * true if any default values have been encountered in the DTD
  112. */
  113. private boolean hadDefaultValue = false;
  114. /**
  115. * Maps each element name to an Integer containing a set of flags.
  116. */
  117. private final Map<String, Integer> elementNameTable = new HashMap<String, Integer>();
  118. /**
  119. * Maps each element name to a List of attribute groups of each attlist declaration.
  120. */
  121. private final Map<String, List<AttributeGroup>> attlistDeclTable = new HashMap<String, List<AttributeGroup>>();
  122. /**
  123. * Set of strings representing names for which there are definitions in the DTD.
  124. */
  125. private final Set<String> definedNames = new HashSet<String>();
  126. /**
  127. * Maps prefixes to namespace URIs.
  128. */
  129. private final Map<String, String> prefixTable = new HashMap<String, String>();
  130. /**
  131. * Maps a string representing an element name to the set of names of attributes
  132. * that have been declated for that element.
  133. */
  134. private final Map<String, Set<String>> attributeNamesTable = new HashMap<String, Set<String>>();
  135. /**
  136. * Contains the set of attribute names that have already been output in the current scope.
  137. */
  138. private Set<String> attributeNames = null;
  139. private String defaultNamespace = null;
  140. private String annotationPrefix = null;
  141. // These variables control the names use for definitions.
  142. private String colonReplacement = null;
  143. private String elementDeclPattern = null;
  144. private String attlistDeclPattern = null;
  145. private String anyName = null;
  146. /**
  147. * Flags for element names used in elementDeclTable.
  148. */
  149. private static final int ELEMENT_DECL = 01;
  150. private static final int ATTLIST_DECL = 02;
  151. private static final int ELEMENT_REF = 04;
  152. /**
  153. * Characters that will be considered for use as a replacement for colon in
  154. * a QName. Also used as separators in constructing names of definitions
  155. * corresponding to element declarations and attlist declarations,
  156. */
  157. private static final String SEPARATORS = ".-_";
  158. // # is the category; % is the name in the category
  159. private static final String DEFAULT_PATTERN = "#.%";
  160. private final String[] ELEMENT_KEYWORDS = {
  161. "element", "elem", "e"
  162. };
  163. private final String[] ATTLIST_KEYWORDS = {
  164. "attlist", "attributes", "attribs", "atts", "a"
  165. };
  166. private final String[] ANY_KEYWORDS = {
  167. "any", "ANY", "anyElement"
  168. };
  169. private static abstract class VisitorBase implements TopLevelVisitor {
  170. public void processingInstruction(String target, String value) throws Exception { }
  171. public void comment(String value) throws Exception { }
  172. public void flagDef(String name, Flag flag) throws Exception { }
  173. public void includedSection(Flag flag, TopLevel[] contents)
  174. throws Exception {
  175. for (int i = 0; i < contents.length; i++)
  176. contents[i].accept(this);
  177. }
  178. public void ignoredSection(Flag flag, String contents) throws Exception { }
  179. public void internalEntityDecl(String name, String value) throws Exception { }
  180. public void externalEntityDecl(String name, ExternalId externalId) throws Exception { }
  181. public void notationDecl(String name, ExternalId externalId) throws Exception { }
  182. public void nameSpecDef(String name, NameSpec nameSpec) throws Exception { }
  183. public void overriddenDef(Def def, boolean isDuplicate) throws Exception { }
  184. public void externalIdDef(String name, ExternalId externalId) throws Exception { }
  185. public void externalIdRef(String name, ExternalId externalId,
  186. String uri, String encoding, TopLevel[] contents)
  187. throws Exception {
  188. for (int i = 0; i < contents.length; i++)
  189. contents[i].accept(this);
  190. }
  191. public void paramDef(String name, String value) throws Exception { }
  192. public void attributeDefaultDef(String name, AttributeDefault ad) throws Exception { }
  193. }
  194. private class Analyzer extends VisitorBase implements ModelGroupVisitor,
  195. AttributeGroupVisitor {
  196. public void elementDecl(NameSpec nameSpec, ModelGroup modelGroup)
  197. throws Exception {
  198. noteElementName(nameSpec.getValue(), ELEMENT_DECL);
  199. modelGroup.accept(this);
  200. }
  201. public void attlistDecl(NameSpec nameSpec, AttributeGroup attributeGroup)
  202. throws Exception {
  203. noteElementName(nameSpec.getValue(), ATTLIST_DECL);
  204. noteAttlist(nameSpec.getValue(), attributeGroup);
  205. attributeGroup.accept(this);
  206. }
  207. public void modelGroupDef(String name, ModelGroup modelGroup)
  208. throws Exception {
  209. noteDef(name);
  210. modelGroup.accept(this);
  211. }
  212. public void attributeGroupDef(String name, AttributeGroup attributeGroup)
  213. throws Exception {
  214. noteDef(name);
  215. attributeGroup.accept(this);
  216. }
  217. public void enumGroupDef(String name, EnumGroup enumGroup) {
  218. noteDef(name);
  219. }
  220. public void datatypeDef(String name, Datatype datatype) {
  221. noteDef(name);
  222. }
  223. public void choice(ModelGroup[] members) throws Exception {
  224. for (int i = 0; i < members.length; i++)
  225. members[i].accept(this);
  226. }
  227. public void sequence(ModelGroup[] members) throws Exception {
  228. for (int i = 0; i < members.length; i++)
  229. members[i].accept(this);
  230. }
  231. public void oneOrMore(ModelGroup member) throws Exception {
  232. member.accept(this);
  233. }
  234. public void zeroOrMore(ModelGroup member) throws Exception {
  235. member.accept(this);
  236. }
  237. public void optional(ModelGroup member) throws Exception {
  238. member.accept(this);
  239. }
  240. public void modelGroupRef(String name, ModelGroup modelGroup) {
  241. }
  242. public void elementRef(NameSpec name) {
  243. noteElementName(name.getValue(), ELEMENT_REF);
  244. }
  245. public void pcdata() {
  246. }
  247. public void any() {
  248. hadAny = true;
  249. }
  250. public void attribute(NameSpec nameSpec,
  251. Datatype datatype,
  252. AttributeDefault attributeDefault) {
  253. noteAttribute(nameSpec.getValue(), attributeDefault.getDefaultValue());
  254. }
  255. public void attributeGroupRef(String name, AttributeGroup attributeGroup) {
  256. }
  257. }
  258. private class ComponentOutput extends VisitorBase {
  259. private final List<Component> components;
  260. private final Annotated grammar;
  261. private List<Comment> comments = null;
  262. ComponentOutput(GrammarPattern grammar) {
  263. components = grammar.getComponents();
  264. this.grammar = grammar;
  265. }
  266. void finish() {
  267. if (comments != null)
  268. grammar.getFollowingElementAnnotations().addAll(comments);
  269. }
  270. private void addComponent(Component c) {
  271. if (comments != null) {
  272. if (components.isEmpty())
  273. grammar.getLeadingComments().addAll(comments);
  274. else
  275. c.getLeadingComments().addAll(comments);
  276. comments = null;
  277. }
  278. components.add(c);
  279. }
  280. public void elementDecl(NameSpec nameSpec, ModelGroup modelGroup) throws Exception {
  281. GroupPattern gp = new GroupPattern();
  282. if (options.inlineAttlistDecls) {
  283. List<AttributeGroup> groups = attlistDeclTable.get(nameSpec.getValue());
  284. if (groups != null) {
  285. attributeNames = new HashSet<String>();
  286. AttributeGroupVisitor agv = new AttributeGroupOutput(gp);
  287. for (AttributeGroup group : groups)
  288. group.accept(agv);
  289. }
  290. }
  291. else
  292. gp.getChildren().add(ref(attlistDeclName(nameSpec.getValue())));
  293. Pattern pattern = convert(modelGroup);
  294. if (gp.getChildren().size() > 0) {
  295. if (pattern instanceof GroupPattern)
  296. gp.getChildren().addAll(((GroupPattern)pattern).getChildren());
  297. else
  298. gp.getChildren().add(pattern);
  299. pattern = gp;
  300. }
  301. addComponent(new DefineComponent(elementDeclName(nameSpec.getValue()),
  302. new ElementPattern(convertQName(nameSpec.getValue(), true),
  303. pattern)));
  304. if (!options.inlineAttlistDecls && (nameFlags(nameSpec.getValue()) & ATTLIST_DECL) == 0) {
  305. DefineComponent dc = new DefineComponent(attlistDeclName(nameSpec.getValue()), new EmptyPattern());
  306. dc.setCombine(Combine.INTERLEAVE);
  307. addComponent(dc);
  308. }
  309. if (anyName != null && options.strictAny) {
  310. DefineComponent dc = new DefineComponent(anyName, ref(elementDeclName(nameSpec.getValue())));
  311. dc.setCombine(Combine.CHOICE);
  312. addComponent(dc);
  313. }
  314. }
  315. public void attlistDecl(NameSpec nameSpec, AttributeGroup attributeGroup) throws Exception {
  316. if (options.inlineAttlistDecls)
  317. return;
  318. String name = nameSpec.getValue();
  319. attributeNames
  320. = attributeNamesTable.get(name);
  321. if (attributeNames == null) {
  322. attributeNames = new HashSet<String>();
  323. attributeNamesTable.put(name, attributeNames);
  324. }
  325. Pattern pattern = convert(attributeGroup);
  326. if (pattern instanceof EmptyPattern) {
  327. // Only keep an empty definition if this is the first attlist for this element,
  328. // and all attlists are also empty. In this case, if we didn't keep the
  329. // definition, we would have no definition for the attlist.
  330. List<AttributeGroup> decls = attlistDeclTable.get(name);
  331. if (decls.get(0) != attributeGroup)
  332. return;
  333. attributeNames = new HashSet<String>();
  334. for (int i = 1, len = decls.size(); i < len; i++)
  335. if (!(convert(decls.get(i)) instanceof EmptyPattern))
  336. return;
  337. }
  338. DefineComponent dc = new DefineComponent(attlistDeclName(name), pattern);
  339. dc.setCombine(Combine.INTERLEAVE);
  340. addComponent(dc);
  341. }
  342. public void modelGroupDef(String name, ModelGroup modelGroup)
  343. throws Exception {
  344. addComponent(new DefineComponent(name, convert(modelGroup)));
  345. }
  346. public void attributeGroupDef(String name, AttributeGroup attributeGroup)
  347. throws Exception {
  348. // This takes care of duplicates within the group
  349. attributeNames = new HashSet<String>();
  350. Pattern pattern;
  351. AttributeGroupMember[] members = attributeGroup.getMembers();
  352. GroupPattern group = new GroupPattern();
  353. AttributeGroupVisitor agv = new AttributeGroupOutput(group);
  354. for (int i = 0; i < members.length; i++)
  355. members[i].accept(agv);
  356. switch (group.getChildren().size()) {
  357. case 0:
  358. pattern = new EmptyPattern();
  359. break;
  360. case 1:
  361. pattern = group.getChildren().get(0);
  362. break;
  363. default:
  364. pattern = group;
  365. break;
  366. }
  367. addComponent(new DefineComponent(name, pattern));
  368. }
  369. public void enumGroupDef(String name, EnumGroup enumGroup) throws Exception {
  370. ChoicePattern choice = new ChoicePattern();
  371. enumGroup.accept(new EnumGroupOutput(choice));
  372. Pattern pattern;
  373. switch (choice.getChildren().size()) {
  374. case 0:
  375. pattern = new NotAllowedPattern();
  376. break;
  377. case 1:
  378. pattern = choice.getChildren().get(0);
  379. break;
  380. default:
  381. pattern = choice;
  382. break;
  383. }
  384. addComponent(new DefineComponent(name, pattern));
  385. }
  386. public void datatypeDef(String name, Datatype datatype) throws Exception {
  387. addComponent(new DefineComponent(name, convert(datatype)));
  388. }
  389. public void comment(String value) {
  390. if (comments == null)
  391. comments = new Vector<Comment>();
  392. comments.add(new Comment(CommentTrimmer.trimComment(value)));
  393. }
  394. public void externalIdRef(String name, ExternalId externalId,
  395. String uri, String encoding, TopLevel[] contents)
  396. throws Exception {
  397. if (uri == null) {
  398. // I don't think this can happen
  399. super.externalIdRef(name, externalId, uri, encoding, contents);
  400. return;
  401. }
  402. SignificanceDetector sd = new SignificanceDetector();
  403. try {
  404. sd.externalIdRef(name, externalId, uri, encoding, contents);
  405. if (!sd.significant)
  406. return;
  407. }
  408. catch (Exception e) {
  409. throw (RuntimeException)e;
  410. }
  411. if (sc.getSchemaDocumentMap().get(uri) != null) {
  412. // I don't think this can happen because the second and subsequent inclusions
  413. // will never pass the SignificanceDetector, but just in case
  414. super.externalIdRef(name, externalId, uri, encoding, contents);
  415. return;
  416. }
  417. IncludeComponent ic = new IncludeComponent(uri);
  418. ic.setNs(defaultNamespace);
  419. addComponent(ic);
  420. GrammarPattern included = new GrammarPattern();
  421. ComponentOutput co = new ComponentOutput(included);
  422. for (int i = 0; i < contents.length; i++)
  423. contents[i].accept(co);
  424. co.finish();
  425. sc.getSchemaDocumentMap().put(uri, new SchemaDocument(included, encoding));
  426. }
  427. }
  428. private class AttributeGroupOutput implements AttributeGroupVisitor {
  429. final List<Pattern> group;
  430. AttributeGroupOutput(GroupPattern gp) {
  431. group = gp.getChildren();
  432. }
  433. public void attribute(NameSpec nameSpec,
  434. Datatype datatype,
  435. AttributeDefault attributeDefault) throws Exception {
  436. String name = nameSpec.getValue();
  437. if (attributeNames.contains(name))
  438. return;
  439. attributeNames.add(name);
  440. if (name.equals("xmlns") || name.startsWith("xmlns:"))
  441. return;
  442. String dv = attributeDefault.getDefaultValue();
  443. String fv = attributeDefault.getFixedValue();
  444. Pattern dt;
  445. if (fv != null) {
  446. String[] typeName = valueType(datatype);
  447. dt = new ValuePattern(typeName[0], typeName[1], fv);
  448. }
  449. else if (datatype.getType() != Datatype.CDATA)
  450. dt = convert(datatype);
  451. else
  452. dt = new TextPattern();
  453. AttributePattern pattern = new AttributePattern(convertQName(name, false), dt);
  454. if (dv != null) {
  455. AttributeAnnotation anno = new AttributeAnnotation(WellKnownNamespaces.RELAX_NG_COMPATIBILITY_ANNOTATIONS, "defaultValue", dv);
  456. anno.setPrefix(annotationPrefix);
  457. pattern.getAttributeAnnotations().add(anno);
  458. }
  459. if (!attributeDefault.isRequired())
  460. group.add(new OptionalPattern(pattern));
  461. else
  462. group.add(pattern);
  463. }
  464. public void attributeGroupRef(String name, AttributeGroup attributeGroup)
  465. throws Exception {
  466. DuplicateAttributeDetector detector = new DuplicateAttributeDetector();
  467. attributeGroup.accept(detector);
  468. if (detector.containsDuplicate)
  469. attributeGroup.accept(this);
  470. else {
  471. group.add(ref(name));
  472. attributeNames.addAll(detector.names);
  473. }
  474. }
  475. }
  476. private class DatatypeOutput implements DatatypeVisitor {
  477. Pattern pattern;
  478. public void cdataDatatype() {
  479. pattern = new DataPattern("", "string");
  480. }
  481. public void tokenizedDatatype(String typeName) {
  482. pattern = new DataPattern(WellKnownNamespaces.XML_SCHEMA_DATATYPES, typeName);
  483. }
  484. public void enumDatatype(EnumGroup enumGroup) throws Exception {
  485. if (enumGroup.getMembers().length == 0)
  486. pattern = new NotAllowedPattern();
  487. else {
  488. ChoicePattern tem = new ChoicePattern();
  489. pattern = tem;
  490. enumGroup.accept(new EnumGroupOutput(tem));
  491. }
  492. }
  493. public void notationDatatype(EnumGroup enumGroup) throws Exception {
  494. enumDatatype(enumGroup);
  495. }
  496. public void datatypeRef(String name, Datatype datatype) {
  497. pattern = ref(name);
  498. }
  499. }
  500. private class EnumGroupOutput implements EnumGroupVisitor {
  501. final private List<Pattern> list;
  502. EnumGroupOutput(ChoicePattern choice) {
  503. list = choice.getChildren();
  504. }
  505. public void enumValue(String value) {
  506. list.add(new ValuePattern("", "token", value));
  507. }
  508. public void enumGroupRef(String name, EnumGroup enumGroup) {
  509. list.add(ref(name));
  510. }
  511. }
  512. private class ModelGroupOutput implements ModelGroupVisitor {
  513. private Pattern pattern;
  514. public void choice(ModelGroup[] members) throws Exception {
  515. if (members.length == 0)
  516. pattern = new NotAllowedPattern();
  517. else if (members.length == 1)
  518. members[0].accept(this);
  519. else {
  520. ChoicePattern tem = new ChoicePattern();
  521. pattern = tem;
  522. List<Pattern> children = tem.getChildren();
  523. for (int i = 0; i < members.length; i++)
  524. children.add(convert(members[i]));
  525. }
  526. }
  527. public void sequence(ModelGroup[] members) throws Exception {
  528. if (members.length == 0)
  529. pattern = new EmptyPattern();
  530. else if (members.length == 1)
  531. members[0].accept(this);
  532. else {
  533. GroupPattern tem = new GroupPattern();
  534. pattern = tem;
  535. List<Pattern> children = tem.getChildren();
  536. for (int i = 0; i < members.length; i++)
  537. children.add(convert(members[i]));
  538. }
  539. }
  540. public void oneOrMore(ModelGroup member) throws Exception {
  541. pattern = new OneOrMorePattern(convert(member));
  542. }
  543. public void zeroOrMore(ModelGroup member) throws Exception {
  544. pattern = new ZeroOrMorePattern(convert(member));
  545. }
  546. public void optional(ModelGroup member) throws Exception {
  547. pattern = new OptionalPattern(convert(member));
  548. }
  549. public void modelGroupRef(String name, ModelGroup modelGroup) {
  550. pattern = ref(name);
  551. }
  552. public void elementRef(NameSpec name) {
  553. pattern = ref(elementDeclName(name.getValue()));
  554. }
  555. public void pcdata() {
  556. pattern = new TextPattern();
  557. }
  558. public void any() {
  559. pattern = ref(anyName);
  560. if (options.strictAny)
  561. pattern = new ZeroOrMorePattern(pattern);
  562. }
  563. }
  564. private class DuplicateAttributeDetector implements AttributeGroupVisitor {
  565. private boolean containsDuplicate = false;
  566. private final List<String> names = new Vector<String>();
  567. public void attribute(NameSpec nameSpec,
  568. Datatype datatype,
  569. AttributeDefault attributeDefault) {
  570. String name = nameSpec.getValue();
  571. if (attributeNames.contains(name))
  572. containsDuplicate = true;
  573. names.add(name);
  574. }
  575. public void attributeGroupRef(String name, AttributeGroup attributeGroup) throws Exception {
  576. attributeGroup.accept(this);
  577. }
  578. }
  579. private class SignificanceDetector extends VisitorBase {
  580. boolean significant = false;
  581. public void elementDecl(NameSpec nameSpec, ModelGroup modelGroup)
  582. throws Exception {
  583. significant = true;
  584. }
  585. public void attlistDecl(NameSpec nameSpec, AttributeGroup attributeGroup)
  586. throws Exception {
  587. significant = true;
  588. }
  589. public void modelGroupDef(String name, ModelGroup modelGroup)
  590. throws Exception {
  591. significant = true;
  592. }
  593. public void attributeGroupDef(String name, AttributeGroup attributeGroup)
  594. throws Exception {
  595. significant = true;
  596. }
  597. public void enumGroupDef(String name, EnumGroup enumGroup) {
  598. significant = true;
  599. }
  600. public void datatypeDef(String name, Datatype datatype) {
  601. significant = true;
  602. }
  603. }
  604. public BufferDtdConverter(Dtd dtd, ErrorReporter er, Options options) {
  605. this.dtd = dtd;
  606. this.er = er;
  607. this.options = options;
  608. }
  609. public SchemaCollection convert() {
  610. try {
  611. dtd.accept(new Analyzer());
  612. chooseNames();
  613. GrammarPattern grammar = new GrammarPattern();
  614. sc.setMainUri(dtd.getUri());
  615. sc.getSchemaDocumentMap().put(dtd.getUri(),
  616. new SchemaDocument(grammar, dtd.getEncoding()));
  617. ComponentOutput co = new ComponentOutput(grammar);
  618. dtd.accept(co);
  619. outputUndefinedElements(grammar.getComponents());
  620. if (options.generateStart)
  621. outputStart(grammar.getComponents());
  622. outputAny(grammar.getComponents());
  623. co.finish();
  624. return sc;
  625. }
  626. catch (Exception e) {
  627. throw (RuntimeException)e;
  628. }
  629. }
  630. private void chooseNames() {
  631. chooseAny();
  632. chooseColonReplacement();
  633. chooseDeclPatterns();
  634. choosePrefixes();
  635. chooseAnnotationPrefix();
  636. }
  637. private void chooseAny() {
  638. if (!hadAny)
  639. return;
  640. if (options.anyName != null) {
  641. if (!definedNames.contains(options.anyName)) {
  642. anyName = options.anyName;
  643. definedNames.add(anyName);
  644. return;
  645. }
  646. warning("cannot_use_any_name");
  647. }
  648. for (int n = 0;; n++) {
  649. for (int i = 0; i < ANY_KEYWORDS.length; i++) {
  650. anyName = repeatChar('_', n) + ANY_KEYWORDS[i];
  651. if (!definedNames.contains(anyName)) {
  652. definedNames.add(anyName);
  653. return;
  654. }
  655. }
  656. }
  657. }
  658. private void choosePrefixes() {
  659. if (options.defaultNamespace != null) {
  660. if (defaultNamespace != null && !defaultNamespace.equals(options.defaultNamespace))
  661. warning("default_namespace_conflict");
  662. defaultNamespace = options.defaultNamespace;
  663. }
  664. else if (defaultNamespace == null)
  665. defaultNamespace = NameClass.INHERIT_NS;
  666. for (Map.Entry<String, String> entry : options.prefixMap.entrySet()) {
  667. String prefix = entry.getKey();
  668. String ns = entry.getValue();
  669. String s = prefixTable.get(prefix);
  670. if (s == null)
  671. warning("irrelevant_prefix", prefix);
  672. else {
  673. if (!s.equals("") && !s.equals(ns))
  674. warning("prefix_conflict", prefix);
  675. prefixTable.put(prefix, ns);
  676. }
  677. }
  678. }
  679. private void chooseAnnotationPrefix() {
  680. if (!hadDefaultValue)
  681. return;
  682. if (options.annotationPrefix != null) {
  683. if (prefixTable.get(options.annotationPrefix) == null) {
  684. annotationPrefix = options.annotationPrefix;
  685. return;
  686. }
  687. warning("cannot_use_annotation_prefix");
  688. }
  689. for (int n = 0;; n++) {
  690. annotationPrefix = repeatChar('_', n) + "a";
  691. if (prefixTable.get(annotationPrefix) == null)
  692. return;
  693. }
  694. }
  695. private void chooseColonReplacement() {
  696. if (options.colonReplacement != null) {
  697. colonReplacement = options.colonReplacement;
  698. if (colonReplacementOk())
  699. return;
  700. warning("cannot_use_colon_replacement");
  701. colonReplacement = null;
  702. }
  703. if (colonReplacementOk())
  704. return;
  705. for (int n = 1;; n++) {
  706. for (int i = 0; i < SEPARATORS.length(); i++) {
  707. colonReplacement = repeatChar(SEPARATORS.charAt(i), n);
  708. if (colonReplacementOk())
  709. return;
  710. }
  711. }
  712. }
  713. private boolean colonReplacementOk() {
  714. Set<String> names = new HashSet<String>();
  715. for (String s : elementNameTable.keySet()) {
  716. String name = mungeQName(s);
  717. if (names.contains(name))
  718. return false;
  719. names.add(name);
  720. }
  721. return true;
  722. }
  723. private void chooseDeclPatterns() {
  724. if (options.elementDeclPattern != null) {
  725. if (patternOk(options.elementDeclPattern, null))
  726. elementDeclPattern = options.elementDeclPattern;
  727. else
  728. warning("cannot_use_element_decl_pattern");
  729. }
  730. if (options.attlistDeclPattern != null) {
  731. if (patternOk(options.attlistDeclPattern, elementDeclPattern))
  732. attlistDeclPattern = options.attlistDeclPattern;
  733. else
  734. warning("cannot_use_attlist_decl_pattern");
  735. }
  736. if (elementDeclPattern != null && attlistDeclPattern != null)
  737. return;
  738. // XXX Try to match length and case of best prefix
  739. String pattern = namingPattern();
  740. if (elementDeclPattern == null) {
  741. if (patternOk("%", attlistDeclPattern))
  742. elementDeclPattern = "%";
  743. else
  744. elementDeclPattern = choosePattern(pattern, ELEMENT_KEYWORDS, attlistDeclPattern);
  745. }
  746. if (attlistDeclPattern == null)
  747. attlistDeclPattern = choosePattern(pattern, ATTLIST_KEYWORDS, elementDeclPattern);
  748. }
  749. private String choosePattern(String metaPattern, String[] keywords, String otherPattern) {
  750. for (;;) {
  751. for (int i = 0; i < keywords.length; i++) {
  752. String pattern = substitute(metaPattern, '#', keywords[i]);
  753. if (patternOk(pattern, otherPattern))
  754. return pattern;
  755. }
  756. // add another separator
  757. metaPattern = (metaPattern.substring(0, 1)
  758. + metaPattern.substring(1, 2)
  759. + metaPattern.substring(1, 2)
  760. + metaPattern.substring(2));
  761. }
  762. }
  763. private String namingPattern() {
  764. Map<String, Integer> patternTable = new HashMap<String, Integer>();
  765. for (String name : definedNames) {
  766. for (int i = 0; i < SEPARATORS.length(); i++) {
  767. char sep = SEPARATORS.charAt(i);
  768. int k = name.indexOf(sep);
  769. if (k > 0)
  770. inc(patternTable, name.substring(0, k + 1) + "%");
  771. k = name.lastIndexOf(sep);
  772. if (k >= 0 && k < name.length() - 1)
  773. inc(patternTable, "%" + name.substring(k));
  774. }
  775. }
  776. String bestPattern = null;
  777. int bestCount = 0;
  778. for (Map.Entry<String, Integer> entry : patternTable.entrySet()) {
  779. int count = entry.getValue();
  780. if (bestPattern == null || count > bestCount) {
  781. bestCount = count;
  782. bestPattern = entry.getKey();
  783. }
  784. }
  785. if (bestPattern == null)
  786. return DEFAULT_PATTERN;
  787. if (bestPattern.charAt(0) == '%')
  788. return bestPattern.substring(0, 2) + "#";
  789. else
  790. return "#" + bestPattern.substring(bestPattern.length() - 2);
  791. }
  792. private static void inc(Map<String, Integer> table, String str) {
  793. Integer n = table.get(str);
  794. if (n == null)
  795. table.put(str, 1);
  796. else
  797. table.put(str, n + 1);
  798. }
  799. private boolean patternOk(String pattern, String otherPattern) {
  800. Set<String> usedNames = new HashSet<String>();
  801. for (String s : elementNameTable.keySet()) {
  802. String name = mungeQName(s);
  803. String declName = substitute(pattern, '%', name);
  804. if (definedNames.contains(declName))
  805. return false;
  806. if (otherPattern != null) {
  807. String otherDeclName = substitute(otherPattern, '%', name);
  808. if (usedNames.contains(declName)
  809. || usedNames.contains(otherDeclName)
  810. || declName.equals(otherDeclName))
  811. return false;
  812. usedNames.add(declName);
  813. usedNames.add(otherDeclName);
  814. }
  815. }
  816. return true;
  817. }
  818. private void noteDef(String name) {
  819. definedNames.add(name);
  820. }
  821. private void noteElementName(String name, int flags) {
  822. Integer n = elementNameTable.get(name);
  823. if (n != null) {
  824. flags |= n;
  825. if (n == flags)
  826. return;
  827. }
  828. else
  829. noteNamePrefix(name);
  830. elementNameTable.put(name, flags);
  831. }
  832. private void noteAttlist(String name, AttributeGroup group) {
  833. List<AttributeGroup> groups = attlistDeclTable.get(name);
  834. if (groups == null) {
  835. groups = new Vector<AttributeGroup>();
  836. attlistDeclTable.put(name, groups);
  837. }
  838. groups.add(group);
  839. }
  840. private void noteAttribute(String name, String defaultValue) {
  841. if (name.equals("xmlns")) {
  842. if (defaultValue != null) {
  843. if (defaultNamespace != null
  844. && !defaultNamespace.equals(defaultValue))
  845. error("INCONSISTENT_DEFAULT_NAMESPACE");
  846. else
  847. defaultNamespace = defaultValue;
  848. }
  849. }
  850. else if (name.startsWith("xmlns:")) {
  851. if (defaultValue != null) {
  852. String prefix = name.substring(6);
  853. String ns = prefixTable.get(prefix);
  854. if (ns != null
  855. && !ns.equals("")
  856. && !ns.equals(defaultValue))
  857. error("INCONSISTENT_PREFIX", prefix);
  858. else if (!prefix.equals("xml"))
  859. prefixTable.put(prefix, defaultValue);
  860. }
  861. }
  862. else {
  863. if (defaultValue != null)
  864. hadDefaultValue = true;
  865. noteNamePrefix(name);
  866. }
  867. }
  868. private void noteNamePrefix(String name) {
  869. int i = name.indexOf(':');
  870. if (i < 0)
  871. return;
  872. String prefix = name.substring(0, i);
  873. if (prefixTable.get(prefix) == null && !prefix.equals("xml"))
  874. prefixTable.put(prefix, "");
  875. }
  876. private int nameFlags(String name) {
  877. Integer n = elementNameTable.get(name);
  878. if (n == null)
  879. return 0;
  880. return n;
  881. }
  882. private String elementDeclName(String name) {
  883. return substitute(elementDeclPattern, '%', mungeQName(name));
  884. }
  885. private String attlistDeclName(String name) {
  886. return substitute(attlistDeclPattern, '%', mungeQName(name));
  887. }
  888. private String mungeQName(String name) {
  889. if (colonReplacement == null) {
  890. int i = name.indexOf(':');
  891. if (i < 0)
  892. return name;
  893. return name.substring(i + 1);
  894. }
  895. return substitute(name, ':', colonReplacement);
  896. }
  897. private static String repeatChar(char c, int n) {
  898. char[] buf = new char[n];
  899. for (int i = 0; i < n; i++)
  900. buf[i] = c;
  901. return new String(buf);
  902. }
  903. /* Replace the first occurrence of ch in pattern by value. */
  904. private static String substitute(String pattern, char ch, String value) {
  905. int i = pattern.indexOf(ch);
  906. if (i < 0)
  907. return pattern;
  908. StringBuffer buf = new StringBuffer();
  909. buf.append(pattern.substring(0, i));
  910. buf.append(value);
  911. buf.append(pattern.substring(i + 1));
  912. return buf.toString();
  913. }
  914. private void outputStart(List<Component> components) {
  915. ChoicePattern choice = new ChoicePattern();
  916. // Use the defined but unreferenced elements.
  917. // If there aren't any, use all defined elements.
  918. int mask = ELEMENT_REF|ELEMENT_DECL;
  919. for (;;) {
  920. boolean gotOne = false;
  921. for (Map.Entry<String, Integer> entry : elementNameTable.entrySet()) {
  922. if ((entry.getValue() & mask) == ELEMENT_DECL) {
  923. gotOne = true;
  924. choice.getChildren().add(ref(elementDeclName(entry.getKey())));
  925. }
  926. }
  927. if (gotOne)
  928. break;
  929. if (mask == ELEMENT_DECL)
  930. return;
  931. mask = ELEMENT_DECL;
  932. }
  933. components.add(new DefineComponent(DefineComponent.START, choice));
  934. }
  935. private void outputAny(List<Component> components) {
  936. if (!hadAny)
  937. return;
  938. if (options.strictAny) {
  939. DefineComponent dc = new DefineComponent(anyName, new TextPattern());
  940. dc.setCombine(Combine.CHOICE);
  941. components.add(dc);
  942. }
  943. else {
  944. // any = (element * { attribute * { text }*, any } | text)*
  945. CompositePattern group = new GroupPattern();
  946. group.getChildren().add(new ZeroOrMorePattern(new AttributePattern(new AnyNameNameClass(),
  947. new TextPattern())));
  948. group.getChildren().add(ref(anyName));
  949. CompositePattern choice = new ChoicePattern();
  950. choice.getChildren().add(new ElementPattern(new AnyNameNameClass(), group));
  951. choice.getChildren().add(new TextPattern());
  952. components.add(new DefineComponent(anyName, new ZeroOrMorePattern(choice)));
  953. }
  954. }
  955. private void outputUndefinedElements(List<Component> components) {
  956. List<String> elementNames = new Vector<String>();
  957. elementNames.addAll(elementNameTable.keySet());
  958. Collections.sort(elementNames);
  959. for (String elementName : elementNames) {
  960. if ((elementNameTable.get(elementName) & ELEMENT_DECL) == 0) {
  961. DefineComponent dc = new DefineComponent(elementDeclName(elementName), new NotAllowedPattern());
  962. dc.setCombine(Combine.CHOICE);
  963. components.add(dc);
  964. }
  965. }
  966. }
  967. static private Pattern ref(String name) {
  968. return new RefPattern(name);
  969. }
  970. private void error(String key) {
  971. er.error(key, null);
  972. }
  973. private void error(String key, String arg) {
  974. er.error(key, arg, null);
  975. }
  976. private void warning(String key) {
  977. er.warning(key, null);
  978. }
  979. private void warning(String key, String arg) {
  980. er.warning(key, arg, null);
  981. }
  982. private static String[] valueType(Datatype datatype) {
  983. datatype = datatype.deref();
  984. switch (datatype.getType()) {
  985. case Datatype.CDATA:
  986. return new String[] { "", "string" };
  987. case Datatype.TOKENIZED:
  988. return new String[] { WellKnownNamespaces.XML_SCHEMA_DATATYPES, ((TokenizedDatatype)datatype).getTypeName() };
  989. }
  990. return new String[] { "", "token" };
  991. }
  992. private Pattern convert(ModelGroup mg) throws Exception {
  993. ModelGroupOutput mgo = new ModelGroupOutput();
  994. mg.accept(mgo);
  995. return mgo.pattern;
  996. }
  997. private Pattern convert(Datatype dt) throws Exception {
  998. DatatypeOutput dto = new DatatypeOutput();
  999. dt.accept(dto);
  1000. return dto.pattern;
  1001. }
  1002. private Pattern convert(AttributeGroup ag) throws Exception {
  1003. GroupPattern group = new GroupPattern();
  1004. ag.accept(new AttributeGroupOutput(group));
  1005. switch (group.getChildren().size()) {
  1006. case 0:
  1007. return new EmptyPattern();
  1008. case 1:
  1009. return group.getChildren().get(0);
  1010. }
  1011. return group;
  1012. }
  1013. private NameClass convertQName(String name, boolean useDefault) {
  1014. int i = name.indexOf(':');
  1015. if (i < 0)
  1016. return new NameNameClass(useDefault ? defaultNamespace : "", name);
  1017. String prefix = name.substring(0, i);
  1018. String localName = name.substring(i + 1);
  1019. String ns;
  1020. if (prefix.equals("xml"))
  1021. ns = WellKnownNamespaces.XML;
  1022. else {
  1023. ns = prefixTable.get(prefix);
  1024. if (ns.equals("")) {
  1025. error("UNDECLARED_PREFIX", prefix);
  1026. ns = "##" + prefix;
  1027. prefixTable.put(prefix, ns);
  1028. }
  1029. }
  1030. NameNameClass nnc = new NameNameClass(ns, localName);
  1031. nnc.setPrefix(prefix);
  1032. return nnc;
  1033. }
  1034. }