1 package org.unicode.cldr.unittest; 2 3 import java.util.Arrays; 4 import java.util.Collection; 5 import java.util.Collections; 6 import java.util.HashMap; 7 import java.util.HashSet; 8 import java.util.LinkedHashSet; 9 import java.util.Map; 10 import java.util.Set; 11 import java.util.TreeMap; 12 import java.util.TreeSet; 13 14 import org.unicode.cldr.util.CLDRConfig; 15 import org.unicode.cldr.util.CLDRFile; 16 import org.unicode.cldr.util.DtdType; 17 import org.unicode.cldr.util.ElementAttributeInfo; 18 import org.unicode.cldr.util.XPathParts; 19 20 import com.ibm.icu.impl.Relation; 21 import com.ibm.icu.impl.Row; 22 import com.ibm.icu.impl.Row.R2; 23 import com.ibm.icu.impl.Row.R3; 24 import com.ibm.icu.impl.Utility; 25 26 public class TestDTDAttributes extends TestFmwkPlus { 27 28 static CLDRConfig testInfo = CLDRConfig.getInstance(); 29 30 /** 31 * Simple test that loads each file in the cldr directory, thus verifying 32 * that the DTD works, and also checks that the PrettyPaths work. 33 * 34 * @author markdavis 35 */ 36 main(String[] args)37 public static void main(String[] args) { 38 new TestDTDAttributes().run(args); 39 } 40 TestOrdering()41 public void TestOrdering() { // Not deemed to be a useful test at this time 42 // - JCE 43 return; 44 // for (DtdType type : DtdType.values()) { 45 // checkOrdering(type); 46 // } 47 } 48 checkOrdering(DtdType type)49 private void checkOrdering(DtdType type) { 50 Relation<String, String> toChildren = ElementAttributeInfo.getInstance( 51 type).getElement2Children(); 52 // Relation<String, String> toParents = 53 // ElementAttributeInfo.getElement2Parents(type); 54 Set<String> containsOrdered = new LinkedHashSet<String>(); 55 for (String element : toChildren.keySet()) { 56 int ordered = 0; 57 Set<String> containedElements = toChildren.getAll(element); 58 for (String contained : containedElements) { 59 if (contained.equals("cp")) 60 continue; 61 int currentOrdered = CLDRFile.isOrdered(contained, null) ? 1 62 : -1; 63 if (currentOrdered > 0) { 64 containsOrdered.add(element); 65 } 66 if (ordered == 0) { 67 ordered = currentOrdered; 68 } else { 69 if (ordered != currentOrdered) { 70 String error = type + "\tMixed ordering inside\t" 71 + element + ":\tordered:\t"; 72 for (String contained2 : containedElements) { 73 if (contained.equals("cp") 74 || !CLDRFile.isOrdered(contained2, null)) 75 continue; 76 error += " " + contained2; 77 } 78 error += ":\tunordered:\t"; 79 for (String contained2 : containedElements) { 80 if (contained.equals("cp") 81 || CLDRFile.isOrdered(contained2, null)) 82 continue; 83 error += " " + contained2; 84 } 85 errln(error); 86 break; 87 } 88 } 89 } 90 } 91 if (containsOrdered.size() != 0) { 92 logln(" " + "// DTD: " + type); 93 for (String element : containsOrdered) { 94 logln(" " + "// <" + element + "> children"); 95 StringBuilder line = new StringBuilder(" "); 96 for (String contained : toChildren.getAll(element)) { 97 line.append("\"" + contained + "\", "); 98 } 99 logln(line.toString()); 100 } 101 } 102 } 103 TestDistinguishing()104 public void TestDistinguishing() { 105 for (DtdType type : DtdType.values()) { 106 if (type == DtdType.ldmlICU) { 107 continue; 108 } 109 showDistinguishing(type); 110 } 111 } 112 showDistinguishing(DtdType dtdType)113 private void showDistinguishing(DtdType dtdType) { 114 Relation<String, String> elementToAttributes = ElementAttributeInfo 115 .getInstance(dtdType).getElement2Attributes(); 116 Relation<String, String> distinguishingAttributeToElements = Relation 117 .<String, String> of(new TreeMap<String, Set<String>>(), 118 TreeSet.class); 119 Relation<String, String> nondistinguishingAttributeToElements = Relation 120 .<String, String> of(new TreeMap<String, Set<String>>(), 121 TreeSet.class); 122 Relation<String, String> orderedAttributeToElements = Relation 123 .<String, String> of(new TreeMap<String, Set<String>>(), 124 TreeSet.class); 125 Map<R2<String, String>, R3<Set<String>, String, String>> attributeData = ElementAttributeInfo 126 .getInstance(dtdType).getElementAttribute2Data(); 127 128 for (String element : elementToAttributes.keySet()) { 129 boolean isOrdered = CLDRFile.isOrdered(element, dtdType); 130 Set<String> attributes = elementToAttributes.getAll(element); 131 for (String attribute : attributes) { 132 if (isOrdered) { 133 orderedAttributeToElements.put(attribute, element); 134 } 135 if (CLDRFile.isDistinguishing(dtdType, element, attribute)) { 136 distinguishingAttributeToElements.put(attribute, element); 137 } else { 138 nondistinguishingAttributeToElements 139 .put(attribute, element); 140 } 141 } 142 } 143 144 // distinguishing elements should be required 145 // make exception for alt 146 for (String attribute : distinguishingAttributeToElements.keySet()) { 147 if (attribute.equals("alt")) 148 continue; 149 for (String element : distinguishingAttributeToElements 150 .getAll(attribute)) { 151 R3<Set<String>, String, String> attributeInfo = attributeData 152 .get(Row.of(element, attribute)); 153 if (!"REQUIRED".equals(attributeInfo.get1())) { 154 logln(dtdType 155 + "\t" 156 + element 157 + "\t" 158 + attribute 159 + "\tDistinguishing attribute, but not REQUIRED in DTD"); 160 } 161 } 162 } 163 164 Set<String> nondistinguishing = new TreeSet<String>( 165 nondistinguishingAttributeToElements.keySet()); 166 nondistinguishing.removeAll(distinguishingAttributeToElements.keySet()); 167 logln("// " + dtdType + "\tnondistinguishing: " + nondistinguishing); 168 169 Set<String> distinguishing = new TreeSet<String>( 170 distinguishingAttributeToElements.keySet()); 171 nondistinguishing.removeAll(nondistinguishingAttributeToElements 172 .keySet()); 173 logln("// " + dtdType + "\tdistinguishing: " + distinguishing); 174 175 Set<String> both = new TreeSet<String>( 176 distinguishingAttributeToElements.keySet()); 177 both.retainAll(nondistinguishingAttributeToElements.keySet()); 178 logln("// " + dtdType + "\tboth: " + both); 179 180 for (String attribute : distinguishing) { 181 logln("{\"" + "dist" + "\", \"" + dtdType + "\", \"" + "*" 182 + "\", \"" + attribute + "\"},"); 183 } 184 for (String attribute : both) { 185 for (String element : nondistinguishingAttributeToElements 186 .getAll(attribute)) { 187 logln("{\"" + "nondist" + "\", \"" + dtdType + "\", \"" 188 + element + "\", \"" + attribute + "\"},"); 189 } 190 } 191 } 192 TestAttributes()193 public void TestAttributes() { 194 checkAttributes(DtdType.ldml, DtdType.supplementalData); 195 checkAttributes(DtdType.ldml, DtdType.ldmlBCP47); 196 checkAttributes(DtdType.ldmlBCP47, DtdType.supplementalData); 197 } 198 checkAttributes(DtdType dtdType1, DtdType dtdType2)199 private void checkAttributes(DtdType dtdType1, DtdType dtdType2) { 200 Map<R2<String, String>, R3<Set<String>, String, String>> mainData = ElementAttributeInfo 201 .getInstance(dtdType1).getElementAttribute2Data(); 202 Map<R2<String, String>, R3<Set<String>, String, String>> suppData = ElementAttributeInfo 203 .getInstance(dtdType2).getElementAttribute2Data(); 204 Set<R2<String, String>> commonKeys = getCommon(mainData.keySet(), 205 suppData.keySet(), new TreeSet<R2<String, String>>()); 206 Set<R2<String, String>> same = new TreeSet<R2<String, String>>(); 207 for (R2<String, String> key : commonKeys) { 208 R3<Set<String>, String, String> mainValue = mainData.get(key); 209 R3<Set<String>, String, String> suppValue = suppData.get(key); 210 if (mainValue.equals(suppValue)) { 211 same.add(key); 212 } else { 213 errln("Different attribute properties across DTDs" 214 + key.toString() + "\t" + dtdType1.toString() + ":\t" 215 + mainValue.toString() + "\t" + dtdType2.toString() 216 + ":\t" + suppValue.toString()); 217 } 218 } 219 for (R2<String, String> key : same) { 220 logln(dtdType1 + " and " + dtdType2 + ":\t" + key + "\t" 221 + mainData.get(key)); 222 } 223 for (R2<String, String> key : mainData.keySet()) { 224 if (commonKeys.contains(key)) 225 continue; 226 logln(dtdType1 + ":\t" + key + "\t" + mainData.get(key)); 227 } 228 for (R2<String, String> key : suppData.keySet()) { 229 if (commonKeys.contains(key)) 230 continue; 231 logln(dtdType2 + ":\t" + key + "\t" + suppData.get(key)); 232 } 233 } 234 235 @SuppressWarnings({ "rawtypes", "unchecked" }) getCommon(T a, T b, T result)236 private <T extends Collection> T getCommon(T a, T b, T result) { 237 result.addAll(a); 238 result.retainAll(b); 239 return result; 240 } 241 TestElements()242 public void TestElements() { // Not deemed to be a useful test at this time 243 // - JCE 244 // checkElements(DtdType.ldml, DtdType.supplementalData); 245 // checkElements(DtdType.ldml, DtdType.ldmlBCP47); 246 // checkElements(DtdType.ldmlBCP47, DtdType.supplementalData); 247 } 248 checkElements(DtdType dtdType1, DtdType dtdType2)249 private void checkElements(DtdType dtdType1, DtdType dtdType2) { 250 Relation<String, String> mainData = ElementAttributeInfo.getInstance( 251 dtdType1).getElement2Children(); 252 Relation<String, String> suppData = ElementAttributeInfo.getInstance( 253 dtdType2).getElement2Children(); 254 Set<String> commonKeys = getCommon(mainData.keySet(), 255 suppData.keySet(), new TreeSet<String>()); 256 Set<String> same = new TreeSet<String>(); 257 for (String key : commonKeys) { 258 Set<String> mainValues = mainData.getAll(key); 259 Set<String> suppValues = suppData.getAll(key); 260 if (mainValues.equals(suppValues)) { 261 same.add(key); 262 continue; 263 } 264 errln("DTD elements have different children\t" + key + "\t" 265 + dtdType1 + ":\t" + mainValues + "\t" + dtdType2 + ":\t" 266 + suppValues); 267 } 268 for (String key : same) { 269 logln(dtdType1 + " and " + dtdType2 + ":\t" + key + "\t" 270 + mainData.getAll(key)); 271 } 272 for (String key : mainData.keySet()) { 273 if (commonKeys.contains(key)) 274 continue; 275 logln(dtdType1 + ":\t" + key + "\t" + mainData.getAll(key)); 276 } 277 for (String key : suppData.keySet()) { 278 if (commonKeys.contains(key)) 279 continue; 280 logln(dtdType2 + ":\t" + key + "\t" + suppData.getAll(key)); 281 } 282 } 283 TestEmpty()284 public void TestEmpty() { // not deemed to be a useful test at this time - 285 // JCE 286 // for (DtdType type : DtdType.values()) { 287 // checkEmpty(type); 288 // } 289 } 290 291 static final Set<String> COLLATION_SINGLETONS = new HashSet<String>( 292 Arrays.asList(new String[] { "first_non_ignorable", 293 "first_primary_ignorable", "first_secondary_ignorable", 294 "first_tertiary_ignorable", "first_trailing", 295 "first_variable", "last_non_ignorable", 296 "last_primary_ignorable", "last_secondary_ignorable", 297 "last_tertiary_ignorable", "last_trailing last_variable", 298 "last_trailing", "last_variable" })); 299 checkEmpty(DtdType type)300 private void checkEmpty(DtdType type) { 301 Relation<String, String> mainData = ElementAttributeInfo.getInstance( 302 type).getElement2Parents(); 303 Relation<String, String> elementToAttributes = ElementAttributeInfo 304 .getInstance(type).getElement2Attributes(); 305 Map<R2<String, String>, R3<Set<String>, String, String>> eaData = ElementAttributeInfo 306 .getInstance(type).getElementAttribute2Data(); 307 308 Set<String> empties = mainData.getAll("EMPTY"); 309 for (String empty : empties) { 310 if (COLLATION_SINGLETONS.contains(empty)) 311 continue; 312 Set<String> attributes = elementToAttributes.getAll(empty); 313 if (attributes == null) { 314 errln("Is EMPTY but no attributes:\t" + type + ", " + empty); 315 continue; 316 } 317 for (String attribute : attributes) { 318 if (attribute.equals("draft") || attribute.equals("references")) 319 continue; 320 logln(type + ", " + empty + ", " + attribute + ", " 321 + eaData.get(Row.of(empty, attribute))); 322 } 323 } 324 } 325 TestLeaf()326 public void TestLeaf() { // Not deemed to be a useful test at this time - 327 // JCE 328 // for (DtdType type : DtdType.values()) { 329 // checkLeaf(type, "PCDATA"); 330 // } 331 } 332 checkLeaf(DtdType type, String contained)333 private void checkLeaf(DtdType type, String contained) { 334 Relation<String, String> mainData = ElementAttributeInfo.getInstance( 335 type).getElement2Parents(); 336 Relation<String, String> elementToAttributes = ElementAttributeInfo 337 .getInstance(type).getElement2Attributes(); 338 Map<R2<String, String>, R3<Set<String>, String, String>> eaData = ElementAttributeInfo 339 .getInstance(type).getElementAttribute2Data(); 340 341 Set<String> data = mainData.getAll(contained); 342 if (data == null) 343 return; 344 for (String element : data) { 345 Set<String> attributes = elementToAttributes.getAll(element); 346 if (attributes == null) { 347 logln(type + ", " + contained + ", " + element 348 + ", No attributes!"); 349 continue; 350 } 351 for (String attribute : attributes) { 352 if (attribute.equals("draft") || attribute.equals("references")) 353 continue; 354 logln(type + ", " + contained + ", " + element + ", " 355 + attribute + ", " 356 + eaData.get(Row.of(element, attribute))); 357 } 358 } 359 } 360 TestZNodeData()361 public void TestZNodeData() { // Not deemed to be a useful test at this time 362 // - JCE 363 // for (DtdType type : DtdType.values()) { 364 // checkNodeData(type); 365 // } 366 } 367 checkNodeData(DtdType type)368 public void checkNodeData(DtdType type) { 369 CurrentData.NodeData data = CurrentData.fullNodeData.get(type); 370 Relation<String, String> element2Parents = ElementAttributeInfo 371 .getInstance(type).getElement2Parents(); 372 Relation<String, String> element2Children = ElementAttributeInfo 373 .getInstance(type).getElement2Children(); 374 Relation<String, String> elementToAttributes = data.elementToAttributes; 375 376 Relation<String, String> dtdElementToAttributes = ElementAttributeInfo 377 .getInstance(type).getElement2Attributes(); 378 Map<R2<String, String>, R3<Set<String>, String, String>> attributeData = ElementAttributeInfo 379 .getInstance(type).getElementAttribute2Data(); 380 381 Set<String> possibleElements = dtdElementToAttributes.keySet(); 382 Set<String> foundElements = elementToAttributes.keySet(); 383 if (!foundElements.equals(possibleElements)) { 384 Set<String> missing = new TreeSet<String>(possibleElements); 385 missing.removeAll(foundElements); 386 errln(type + "\t" + "Elements defined but not in data:\t" + missing); 387 } 388 389 // attributes not found 390 for (String element : dtdElementToAttributes.keySet()) { 391 Set<String> dtdAttributes = dtdElementToAttributes.getAll(element); 392 Set<String> actualAttributes = remove_q(elementToAttributes 393 .getAll(element)); 394 Set<String> attributesAlwaysFound = remove_q(data.elementToAttributesAlwaysFound 395 .getAll(element)); 396 397 if (!dtdAttributes.containsAll(actualAttributes) 398 || !dtdAttributes.containsAll(attributesAlwaysFound)) { 399 errln(type + "\t" 400 + "Actual attributes exceed DTD attributes:\t" + type 401 + ", " + element + ", " + dtdAttributes + ", " 402 + actualAttributes + ", " + attributesAlwaysFound); 403 } 404 405 Set<String> notFound = new TreeSet<String>(dtdAttributes); 406 notFound.removeAll(actualAttributes); 407 notFound.remove("draft"); 408 notFound.remove("references"); 409 notFound.remove("standard"); 410 notFound.remove("alt"); 411 notFound.remove("validSubLocales"); 412 if (notFound.size() != 0) { 413 warnln(type + "\tAttributes not found for:\t" + element 414 + "\tnotFound:\t" + notFound + "\tfound:\t" 415 + actualAttributes); 416 } 417 418 for (String attributeAlwaysFound : attributesAlwaysFound) { 419 // make sure REQUIRED; not really an error, but for now... 420 R3<Set<String>, String, String> attributeDatum = attributeData 421 .get(Row.of(element, attributeAlwaysFound)); 422 if (attributeDatum == null) { 423 errln(type + "\tData not found for " + type + ", " 424 + element + ", " + attributeAlwaysFound); 425 continue; 426 } 427 if (!"#REQUIRED".equals(attributeDatum.get1())) { 428 if (attributeDatum.get1() == null) { 429 warnln(type 430 + "\tAttribute not REQUIRED but ALWAYS found, element:\t" 431 + element + "\tattribute:\t" 432 + attributeAlwaysFound); 433 } 434 } 435 } 436 } 437 438 Set<String> empties = element2Parents.getAll("EMPTY"); 439 440 Set<String> overlap = getCommon(data.valueNodes.keySet(), 441 data.branchNodes.keySet(), new TreeSet<String>()); 442 for (String s : overlap) { 443 warnln(type + "\tOverlap in value and branch!!\t" + s 444 + "\tvalue:\t" + data.valueNodes.get(s) + "\tbranch:\t" 445 + data.branchNodes.get(s)); 446 } 447 for (String s : data.valueNodes.keySet()) { 448 if (overlap.contains(s)) 449 continue; 450 logln(type + "\tLeaf:\t" + s + "\t\t" + data.valueNodes.get(s)); 451 } 452 for (String s : data.branchNodes.keySet()) { 453 if (overlap.contains(s)) 454 continue; 455 logln(type + "\tBranch:\t" + s + "\t\t" + data.branchNodes.get(s)); 456 } 457 458 overlap = getCommon(data.valueNodes.keySet(), 459 data.valuelessNodes.keySet(), new TreeSet<String>()); 460 for (String s : overlap) { 461 warnln(type + "\tOverlap in value and valueless!!\t" + s 462 + "\tvalue:\t" + data.valueNodes.get(s) + "\tvalueless:\t" 463 + data.valuelessNodes.get(s)); 464 } 465 466 for (String s : data.valueNodes.keySet()) { 467 if (overlap.contains(s)) 468 continue; 469 logln(type + "\tValue:\t" + s + "\t\t" + data.valueNodes.get(s)); 470 } 471 for (String s : data.valuelessNodes.keySet()) { 472 if (overlap.contains(s)) 473 continue; 474 logln(type + "\tValueless:\t" + s + "\t\t" 475 + data.valuelessNodes.get(s)); 476 Set<String> containing = element2Children.getAll(s); 477 if (!empties.contains(s) && containing.contains("PCDATA")) { 478 errln(type + "\t***Should be empty in DTD but isn't:\t" + s 479 + "\t" + containing); 480 } 481 } 482 } 483 remove_q(Set<String> actualAttributes)484 private Set<String> remove_q(Set<String> actualAttributes) { 485 if (actualAttributes == null) { 486 actualAttributes = Collections.emptySet(); 487 } else if (actualAttributes.contains("_q")) { 488 actualAttributes = new LinkedHashSet<String>(actualAttributes); 489 actualAttributes.remove("_q"); 490 } 491 return actualAttributes; 492 } 493 494 static class CurrentData { 495 static class NodeData { 496 final DtdType myType; 497 Map<String, R2<String, String>> branchNodes = new TreeMap<String, R2<String, String>>(); 498 Map<String, R3<String, String, String>> valueNodes = new TreeMap<String, R3<String, String, String>>(); 499 Map<String, R2<String, String>> valuelessNodes = new TreeMap<String, R2<String, String>>(); 500 Relation<String, String> elementToAttributes = Relation 501 .<String, String> of(new TreeMap<String, Set<String>>(), 502 TreeSet.class); 503 Relation<String, String> elementToAttributesAlwaysFound = Relation 504 .<String, String> of(new TreeMap<String, Set<String>>(), 505 TreeSet.class); 506 NodeData(DtdType type)507 NodeData(DtdType type) { 508 myType = type; 509 } 510 } 511 512 static Map<DtdType, NodeData> fullNodeData = new HashMap<DtdType, NodeData>(); 513 static { 514 XPathParts parts = new XPathParts(); 515 for (String locale : testInfo.getCldrFactory().getAvailable()) { 516 CLDRFile file = testInfo.getCLDRFile(locale, false); 517 NodeData nodeData = null; 518 for (String xpath : file) { 519 String value = file.getStringValue(xpath); 520 String fullXpath = file.getFullXPath(xpath); 521 parts.set(fullXpath); 522 if (nodeData == null) { 523 String root = parts.getElement(0); 524 DtdType type = DtdType.valueOf(root); 525 nodeData = fullNodeData.get(type); 526 if (nodeData == null) { fullNodeData.put(type, nodeData = new NodeData( type))527 fullNodeData.put(type, nodeData = new NodeData( 528 type)); 529 } 530 } 531 int last = parts.size() - 1; 532 String element = null; 533 for (int i = 0; i <= last; ++i) { 534 element = parts.getElement(i); 535 Collection<String> attributes = parts 536 .getAttributeKeys(i); nodeData.elementToAttributes.putAll(element, attributes)537 nodeData.elementToAttributes.putAll(element, 538 attributes); 539 Set<String> oldAlways = nodeData.elementToAttributesAlwaysFound 540 .getAll(element); 541 if (oldAlways == null) { nodeData.elementToAttributesAlwaysFound.putAll( element, attributes)542 nodeData.elementToAttributesAlwaysFound.putAll( 543 element, attributes); 544 } else { 545 // need retainAll, removeAll 546 for (String old : new TreeSet<String>(oldAlways)) { 547 if (!attributes.contains(old)) { 548 nodeData.elementToAttributesAlwaysFound remove(element, old)549 .remove(element, old); 550 } 551 } 552 } 553 if (i != last) { putIfNew(nodeData.branchNodes, element, locale, xpath)554 putIfNew(nodeData.branchNodes, element, locale, 555 xpath); 556 } else { 557 if (value.length() > 0) { putIfNew(nodeData.valueNodes, element, value, locale, xpath)558 putIfNew(nodeData.valueNodes, element, 559 value, locale, xpath); 560 } else { putIfNew(nodeData.valuelessNodes, element, locale, xpath)561 putIfNew(nodeData.valuelessNodes, element, 562 locale, xpath); 563 } 564 } 565 } 566 } 567 } 568 } 569 putIfNew(Map<String, R2<String, String>> map, String key, String locale, String xpath)570 static void putIfNew(Map<String, R2<String, String>> map, String key, 571 String locale, String xpath) { 572 if (!map.containsKey(key)) { 573 map.put(key, Row.of(locale, xpath)); 574 } 575 } 576 putIfNew(Map<String, R3<String, String, String>> map, String key, String value, String locale, String xpath)577 static void putIfNew(Map<String, R3<String, String, String>> map, 578 String key, String value, String locale, String xpath) { 579 if (!map.containsKey(key)) { 580 if (value.length() > 30) 581 value = value.substring(0, 30) + "..."; 582 value = value.replace("\n", "\\n"); 583 map.put(key, Row.of(value, locale, xpath)); 584 } 585 } 586 } 587 588 // public void TestStructure() { 589 // for (DtdType type : DtdType.values()) { 590 // logln("*= distinguished, \u2021=ordered, \u2020=default"); 591 // Relation<String, String> toChildren = 592 // ElementAttributeInfo.getInstance(type).getElement2Children(); 593 // Relation<String, String> toAttributes = 594 // ElementAttributeInfo.getInstance(type).getElement2Attributes(); 595 // Map<R2<String, String>, R3<Set<String>, String, String>> toAttributeData 596 // = ElementAttributeInfo 597 // .getInstance(type).getElementAttribute2Data(); 598 // checkStructure(type, type.toString(), 0, toChildren, toAttributes, 599 // toAttributeData); 600 // } 601 // } 602 checkStructure( DtdType dtdType, String element, int indent, Relation<String, String> toChildren, Relation<String, String> toAttributes, Map<R2<String, String>, R3<Set<String>, String, String>> toAttributeData)603 private void checkStructure( 604 DtdType dtdType, 605 String element, 606 int indent, 607 Relation<String, String> toChildren, 608 Relation<String, String> toAttributes, 609 Map<R2<String, String>, R3<Set<String>, String, String>> toAttributeData) { 610 Set<String> myChildren = toChildren.getAll(element); 611 String values = ""; 612 boolean skipChildren = false; 613 if (myChildren != null) { 614 if (myChildren.contains("PCDATA")) { 615 values = "\tVALUE=" + myChildren.toString(); 616 skipChildren = true; 617 } else if (myChildren.contains("EMPTY")) { 618 // values = ";\t\tVALUE=" + myChildren.toString(); 619 skipChildren = true; 620 } 621 } 622 String elementName = element; 623 if (CLDRFile.isOrdered(element, dtdType)) { 624 elementName += "\u2021"; 625 } 626 logln(Utility.repeat("\t", indent) 627 + elementName 628 + checkAttributeStructure(dtdType, element, 629 toAttributes.getAll(element), toAttributeData) 630 + values); 631 if (myChildren == null || skipChildren) 632 return; 633 for (String child : myChildren) { 634 checkStructure(dtdType, child, indent + 1, toChildren, 635 toAttributes, toAttributeData); 636 } 637 } 638 checkAttributeStructure( DtdType dtdType, String element, Set<String> attributes, Map<R2<String, String>, R3<Set<String>, String, String>> toAttributeData)639 private String checkAttributeStructure( 640 DtdType dtdType, String element, 641 Set<String> attributes, 642 Map<R2<String, String>, R3<Set<String>, String, String>> toAttributeData) { 643 if (attributes == null) 644 return ""; 645 String result = ""; 646 for (String attribute : attributes) { 647 if (attribute.equals("alt") || attribute.equals("draft") 648 || attribute.equals("standard") 649 || attribute.equals("references")) 650 continue; 651 if (result.length() != 0) 652 result += "\t"; 653 R3<Set<String>, String, String> data = toAttributeData.get(Row.of( 654 element, attribute)); 655 if (CLDRFile.isDistinguishing(dtdType, element, attribute)) { 656 attribute += "*"; 657 } 658 attribute += "=" 659 + formatForAttribute(data.get0(), data.get1(), data.get2()); 660 result += attribute; 661 } 662 if (result.length() == 0) 663 return result; 664 return "\t" + result; 665 // draft, standard, references, alt 666 } 667 formatForAttribute(Set<String> type, String mode, String value)668 private String formatForAttribute(Set<String> type, String mode, 669 String value) { 670 String first = type.iterator().next(); 671 String typeName = attributeTypes.contains(first) ? first : type 672 .toString(); 673 if (mode == null) { 674 if (value == null) { 675 throw new IllegalArgumentException(type + ", " + mode + ", " 676 + value); 677 } 678 return typeName.replace(value, value + "\u2020"); 679 } 680 if (mode.equals("#FIXED")) 681 return value; 682 if (value != null) { 683 throw new IllegalArgumentException(type + ", " + mode + ", " 684 + value); 685 } 686 if (mode.equals("#REQUIRED")) { 687 return typeName; 688 } 689 if (mode.equals("#IMPLIED")) { 690 return typeName + "?"; 691 } 692 throw new IllegalArgumentException(type + ", " + mode + ", " + value); 693 } 694 695 static Set<String> attributeTypes = new HashSet<String>( 696 Arrays.asList(new String[] { "CDATA", "ID", "IDREF", "IDREFS", 697 "ENTITY", "ENTITIES", "NMTOKEN", "NMTOKENS" })); 698 699 static Set<String> attributeMode = new HashSet<String>( 700 Arrays.asList(new String[] { "#REQUIRED", "#IMPLIED", "#FIXED" })); 701 } 702