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 for (String locale : testInfo.getCldrFactory().getAvailable()) { 515 CLDRFile file = testInfo.getCLDRFile(locale, false); 516 NodeData nodeData = null; 517 for (String xpath : file) { 518 String value = file.getStringValue(xpath); 519 String fullXpath = file.getFullXPath(xpath); 520 XPathParts parts = XPathParts.getFrozenInstance(fullXpath); 521 if (nodeData == null) { 522 String root = parts.getElement(0); 523 DtdType type = DtdType.valueOf(root); 524 nodeData = fullNodeData.get(type); 525 if (nodeData == null) { fullNodeData.put(type, nodeData = new NodeData( type))526 fullNodeData.put(type, nodeData = new NodeData( 527 type)); 528 } 529 } 530 int last = parts.size() - 1; 531 String element = null; 532 for (int i = 0; i <= last; ++i) { 533 element = parts.getElement(i); 534 Collection<String> attributes = parts 535 .getAttributeKeys(i); nodeData.elementToAttributes.putAll(element, attributes)536 nodeData.elementToAttributes.putAll(element, 537 attributes); 538 Set<String> oldAlways = nodeData.elementToAttributesAlwaysFound 539 .getAll(element); 540 if (oldAlways == null) { nodeData.elementToAttributesAlwaysFound.putAll( element, attributes)541 nodeData.elementToAttributesAlwaysFound.putAll( 542 element, attributes); 543 } else { 544 // need retainAll, removeAll 545 for (String old : new TreeSet<String>(oldAlways)) { 546 if (!attributes.contains(old)) { 547 nodeData.elementToAttributesAlwaysFound remove(element, old)548 .remove(element, old); 549 } 550 } 551 } 552 if (i != last) { putIfNew(nodeData.branchNodes, element, locale, xpath)553 putIfNew(nodeData.branchNodes, element, locale, 554 xpath); 555 } else { 556 if (value.length() > 0) { putIfNew(nodeData.valueNodes, element, value, locale, xpath)557 putIfNew(nodeData.valueNodes, element, 558 value, locale, xpath); 559 } else { putIfNew(nodeData.valuelessNodes, element, locale, xpath)560 putIfNew(nodeData.valuelessNodes, element, 561 locale, xpath); 562 } 563 } 564 } 565 } 566 } 567 } 568 putIfNew(Map<String, R2<String, String>> map, String key, String locale, String xpath)569 static void putIfNew(Map<String, R2<String, String>> map, String key, 570 String locale, String xpath) { 571 if (!map.containsKey(key)) { 572 map.put(key, Row.of(locale, xpath)); 573 } 574 } 575 putIfNew(Map<String, R3<String, String, String>> map, String key, String value, String locale, String xpath)576 static void putIfNew(Map<String, R3<String, String, String>> map, 577 String key, String value, String locale, String xpath) { 578 if (!map.containsKey(key)) { 579 if (value.length() > 30) 580 value = value.substring(0, 30) + "..."; 581 value = value.replace("\n", "\\n"); 582 map.put(key, Row.of(value, locale, xpath)); 583 } 584 } 585 } 586 587 // public void TestStructure() { 588 // for (DtdType type : DtdType.values()) { 589 // logln("*= distinguished, \u2021=ordered, \u2020=default"); 590 // Relation<String, String> toChildren = 591 // ElementAttributeInfo.getInstance(type).getElement2Children(); 592 // Relation<String, String> toAttributes = 593 // ElementAttributeInfo.getInstance(type).getElement2Attributes(); 594 // Map<R2<String, String>, R3<Set<String>, String, String>> toAttributeData 595 // = ElementAttributeInfo 596 // .getInstance(type).getElementAttribute2Data(); 597 // checkStructure(type, type.toString(), 0, toChildren, toAttributes, 598 // toAttributeData); 599 // } 600 // } 601 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)602 private void checkStructure( 603 DtdType dtdType, 604 String element, 605 int indent, 606 Relation<String, String> toChildren, 607 Relation<String, String> toAttributes, 608 Map<R2<String, String>, R3<Set<String>, String, String>> toAttributeData) { 609 Set<String> myChildren = toChildren.getAll(element); 610 String values = ""; 611 boolean skipChildren = false; 612 if (myChildren != null) { 613 if (myChildren.contains("PCDATA")) { 614 values = "\tVALUE=" + myChildren.toString(); 615 skipChildren = true; 616 } else if (myChildren.contains("EMPTY")) { 617 // values = ";\t\tVALUE=" + myChildren.toString(); 618 skipChildren = true; 619 } 620 } 621 String elementName = element; 622 if (CLDRFile.isOrdered(element, dtdType)) { 623 elementName += "\u2021"; 624 } 625 logln(Utility.repeat("\t", indent) 626 + elementName 627 + checkAttributeStructure(dtdType, element, 628 toAttributes.getAll(element), toAttributeData) 629 + values); 630 if (myChildren == null || skipChildren) 631 return; 632 for (String child : myChildren) { 633 checkStructure(dtdType, child, indent + 1, toChildren, 634 toAttributes, toAttributeData); 635 } 636 } 637 checkAttributeStructure( DtdType dtdType, String element, Set<String> attributes, Map<R2<String, String>, R3<Set<String>, String, String>> toAttributeData)638 private String checkAttributeStructure( 639 DtdType dtdType, String element, 640 Set<String> attributes, 641 Map<R2<String, String>, R3<Set<String>, String, String>> toAttributeData) { 642 if (attributes == null) 643 return ""; 644 String result = ""; 645 for (String attribute : attributes) { 646 if (attribute.equals("alt") || attribute.equals("draft") 647 || attribute.equals("standard") 648 || attribute.equals("references")) 649 continue; 650 if (result.length() != 0) 651 result += "\t"; 652 R3<Set<String>, String, String> data = toAttributeData.get(Row.of( 653 element, attribute)); 654 if (CLDRFile.isDistinguishing(dtdType, element, attribute)) { 655 attribute += "*"; 656 } 657 attribute += "=" 658 + formatForAttribute(data.get0(), data.get1(), data.get2()); 659 result += attribute; 660 } 661 if (result.length() == 0) 662 return result; 663 return "\t" + result; 664 // draft, standard, references, alt 665 } 666 formatForAttribute(Set<String> type, String mode, String value)667 private String formatForAttribute(Set<String> type, String mode, 668 String value) { 669 String first = type.iterator().next(); 670 String typeName = attributeTypes.contains(first) ? first : type 671 .toString(); 672 if (mode == null) { 673 if (value == null) { 674 throw new IllegalArgumentException(type + ", " + mode + ", " 675 + value); 676 } 677 return typeName.replace(value, value + "\u2020"); 678 } 679 if (mode.equals("#FIXED")) 680 return value; 681 if (value != null) { 682 throw new IllegalArgumentException(type + ", " + mode + ", " 683 + value); 684 } 685 if (mode.equals("#REQUIRED")) { 686 return typeName; 687 } 688 if (mode.equals("#IMPLIED")) { 689 return typeName + "?"; 690 } 691 throw new IllegalArgumentException(type + ", " + mode + ", " + value); 692 } 693 694 static Set<String> attributeTypes = new HashSet<String>( 695 Arrays.asList(new String[] { "CDATA", "ID", "IDREF", "IDREFS", 696 "ENTITY", "ENTITIES", "NMTOKEN", "NMTOKENS" })); 697 698 static Set<String> attributeMode = new HashSet<String>( 699 Arrays.asList(new String[] { "#REQUIRED", "#IMPLIED", "#FIXED" })); 700 } 701