1 /* Copyright 2004 The Apache Software Foundation 2 * 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 package org.apache.xmlbeans.impl.xsd2inst; 17 18 /* 19 * TODO: 20 * Comment on enumerations? 21 * Comment on facets? 22 * Have a verbose option? 23 * Have a sample data option, would create valid instance with sample data? 24 * Add the pattern facet; this is tricky, considering the relationship with length 25 */ 26 27 import org.apache.xmlbeans.SchemaType; 28 import org.apache.xmlbeans.XmlCursor; 29 import org.apache.xmlbeans.SchemaParticle; 30 import org.apache.xmlbeans.SchemaLocalElement; 31 import org.apache.xmlbeans.SchemaProperty; 32 import org.apache.xmlbeans.GDuration; 33 import org.apache.xmlbeans.GDurationBuilder; 34 import org.apache.xmlbeans.GDate; 35 import org.apache.xmlbeans.GDateBuilder; 36 import org.apache.xmlbeans.XmlAnySimpleType; 37 import org.apache.xmlbeans.SimpleValue; 38 import org.apache.xmlbeans.XmlOptions; 39 import org.apache.xmlbeans.XmlObject; 40 import org.apache.xmlbeans.XmlInteger; 41 import org.apache.xmlbeans.XmlDate; 42 import org.apache.xmlbeans.XmlDateTime; 43 import org.apache.xmlbeans.XmlTime; 44 import org.apache.xmlbeans.XmlGYear; 45 import org.apache.xmlbeans.XmlGYearMonth; 46 import org.apache.xmlbeans.XmlGMonth; 47 import org.apache.xmlbeans.XmlGMonthDay; 48 import org.apache.xmlbeans.XmlGDay; 49 import org.apache.xmlbeans.XmlDecimal; 50 import org.apache.xmlbeans.XmlDuration; 51 import org.apache.xmlbeans.soap.SchemaWSDLArrayType; 52 import org.apache.xmlbeans.soap.SOAPArrayType; 53 import org.apache.xmlbeans.impl.util.Base64; 54 import org.apache.xmlbeans.impl.util.HexBin; 55 56 import java.math.BigDecimal; 57 import java.math.BigInteger; 58 import java.util.ArrayList; 59 import java.util.Calendar; 60 import java.util.Date; 61 import java.util.Random; 62 import java.util.Set; 63 import java.util.HashSet; 64 import java.util.Arrays; 65 66 import javax.xml.namespace.QName; 67 68 public class SampleXmlUtil 69 { 70 private boolean _soapEnc; 71 private static final int MAX_ELEMENTS = 1000; 72 private int _nElements; 73 74 private SampleXmlUtil(boolean soapEnc) 75 { 76 _soapEnc = soapEnc; 77 } 78 79 public static String createSampleForType(SchemaType sType) 80 { 81 XmlObject object = XmlObject.Factory.newInstance(); 82 XmlCursor cursor = object.newCursor(); 83 // Skip the document node 84 cursor.toNextToken(); 85 // Using the type and the cursor, call the utility method to get a 86 // sample XML payload for that Schema element 87 new SampleXmlUtil(false).createSampleForType(sType, cursor); 88 // Cursor now contains the sample payload 89 // Pretty print the result. Note that the cursor is positioned at the 90 // end of the doc so we use the original xml object that the cursor was 91 // created upon to do the xmlText() against. 92 XmlOptions options = new XmlOptions(); 93 options.put(XmlOptions.SAVE_PRETTY_PRINT); 94 options.put(XmlOptions.SAVE_PRETTY_PRINT_INDENT, 2); 95 options.put(XmlOptions.SAVE_AGGRESSIVE_NAMESPACES); 96 String result = object.xmlText(options); 97 return result; 98 } 99 100 Random _picker = new Random(1); 101 102 /** 103 * Cursor position 104 * Before: 105 * <theElement>^</theElement> 106 * After: 107 * <theElement><lots of stuff/>^</theElement> 108 */ 109 private void createSampleForType(SchemaType stype, XmlCursor xmlc) 110 { 111 if (_typeStack.contains( stype )) 112 return; 113 114 _typeStack.add( stype ); 115 116 try 117 { 118 if (stype.isSimpleType() || stype.isURType()) 119 { 120 processSimpleType(stype, xmlc); 121 return; 122 } 123 124 // complex Type 125 // <theElement>^</theElement> 126 processAttributes(stype, xmlc); 127 128 // <theElement attri1="string">^</theElement> 129 switch (stype.getContentType()) 130 { 131 case SchemaType.NOT_COMPLEX_TYPE : 132 case SchemaType.EMPTY_CONTENT : 133 // noop 134 break; 135 case SchemaType.SIMPLE_CONTENT : 136 { 137 processSimpleType(stype, xmlc); 138 } 139 break; 140 case SchemaType.MIXED_CONTENT : 141 xmlc.insertChars(pick(WORDS) + " "); 142 if (stype.getContentModel() != null) 143 { 144 processParticle(stype.getContentModel(), xmlc, true); 145 } 146 xmlc.insertChars(pick(WORDS)); 147 break; 148 case SchemaType.ELEMENT_CONTENT : 149 if (stype.getContentModel() != null) 150 { 151 processParticle(stype.getContentModel(), xmlc, false); 152 } 153 break; 154 } 155 } 156 finally 157 { 158 _typeStack.remove( _typeStack.size() - 1 ); 159 } 160 } 161 162 private void processSimpleType(SchemaType stype, XmlCursor xmlc) 163 { 164 String sample = sampleDataForSimpleType(stype); 165 xmlc.insertChars(sample); 166 } 167 168 private String sampleDataForSimpleType(SchemaType sType) 169 { 170 if (XmlObject.type.equals(sType)) 171 return "anyType"; 172 173 if (XmlAnySimpleType.type.equals(sType)) 174 return "anySimpleType"; 175 176 if (sType.getSimpleVariety() == SchemaType.LIST) 177 { 178 SchemaType itemType = sType.getListItemType(); 179 StringBuffer sb = new StringBuffer(); 180 int length = pickLength(sType); 181 if (length > 0) 182 sb.append(sampleDataForSimpleType(itemType)); 183 for (int i = 1; i < length; i += 1) 184 { 185 sb.append(' '); 186 sb.append(sampleDataForSimpleType(itemType)); 187 } 188 return sb.toString(); 189 } 190 191 if (sType.getSimpleVariety() == SchemaType.UNION) 192 { 193 SchemaType[] possibleTypes = sType.getUnionConstituentTypes(); 194 if (possibleTypes.length == 0) 195 return ""; 196 return sampleDataForSimpleType(possibleTypes[pick(possibleTypes.length)]); 197 } 198 199 XmlAnySimpleType[] enumValues = sType.getEnumerationValues(); 200 if (enumValues != null && enumValues.length > 0) 201 { 202 return enumValues[pick(enumValues.length)].getStringValue(); 203 } 204 205 switch (sType.getPrimitiveType().getBuiltinTypeCode()) 206 { 207 default: 208 case SchemaType.BTC_NOT_BUILTIN: 209 return ""; 210 211 case SchemaType.BTC_ANY_TYPE: 212 case SchemaType.BTC_ANY_SIMPLE: 213 return "anything"; 214 215 case SchemaType.BTC_BOOLEAN: 216 return pick(2) == 0 ? "true" : "false"; 217 218 case SchemaType.BTC_BASE_64_BINARY: 219 { 220 String result = null; 221 try 222 { result = new String(Base64.encode(formatToLength(pick(WORDS), sType).getBytes("utf-8"))); } 223 catch (java.io.UnsupportedEncodingException e) 224 { /* Can't possibly happen */ } 225 return result; 226 } 227 228 case SchemaType.BTC_HEX_BINARY: 229 return HexBin.encode(formatToLength(pick(WORDS), sType)); 230 231 case SchemaType.BTC_ANY_URI: 232 return formatToLength("http://www." + pick(DNS1) + "." + pick(DNS2) + "/" + pick(WORDS) + "/" + pick(WORDS), sType); 233 234 case SchemaType.BTC_QNAME: 235 return formatToLength("qname", sType); 236 237 case SchemaType.BTC_NOTATION: 238 return formatToLength("notation", sType); 239 240 case SchemaType.BTC_FLOAT: 241 return "1.5E2"; 242 case SchemaType.BTC_DOUBLE: 243 return "1.051732E7"; 244 case SchemaType.BTC_DECIMAL: 245 switch (closestBuiltin(sType).getBuiltinTypeCode()) 246 { 247 case SchemaType.BTC_SHORT: 248 return formatDecimal("1", sType); 249 case SchemaType.BTC_UNSIGNED_SHORT: 250 return formatDecimal("5", sType); 251 case SchemaType.BTC_BYTE: 252 return formatDecimal("2", sType); 253 case SchemaType.BTC_UNSIGNED_BYTE: 254 return formatDecimal("6", sType); 255 case SchemaType.BTC_INT: 256 return formatDecimal("3", sType); 257 case SchemaType.BTC_UNSIGNED_INT: 258 return formatDecimal("7", sType); 259 case SchemaType.BTC_LONG: 260 return formatDecimal("10", sType); 261 case SchemaType.BTC_UNSIGNED_LONG: 262 return formatDecimal("11", sType); 263 case SchemaType.BTC_INTEGER: 264 return formatDecimal("100", sType); 265 case SchemaType.BTC_NON_POSITIVE_INTEGER: 266 return formatDecimal("-200", sType); 267 case SchemaType.BTC_NEGATIVE_INTEGER: 268 return formatDecimal("-201", sType); 269 case SchemaType.BTC_NON_NEGATIVE_INTEGER: 270 return formatDecimal("200", sType); 271 case SchemaType.BTC_POSITIVE_INTEGER: 272 return formatDecimal("201", sType); 273 default: 274 case SchemaType.BTC_DECIMAL: 275 return formatDecimal("1000.00", sType); 276 } 277 278 case SchemaType.BTC_STRING: 279 { 280 String result; 281 switch (closestBuiltin(sType).getBuiltinTypeCode()) 282 { 283 case SchemaType.BTC_STRING: 284 case SchemaType.BTC_NORMALIZED_STRING: 285 result = "string"; 286 break; 287 288 case SchemaType.BTC_TOKEN: 289 result = "token"; 290 break; 291 292 default: 293 result = "string"; 294 break; 295 } 296 297 return formatToLength(result, sType); 298 } 299 300 case SchemaType.BTC_DURATION: 301 return formatDuration(sType); 302 303 case SchemaType.BTC_DATE_TIME: 304 case SchemaType.BTC_TIME: 305 case SchemaType.BTC_DATE: 306 case SchemaType.BTC_G_YEAR_MONTH: 307 case SchemaType.BTC_G_YEAR: 308 case SchemaType.BTC_G_MONTH_DAY: 309 case SchemaType.BTC_G_DAY: 310 case SchemaType.BTC_G_MONTH: 311 return formatDate(sType); 312 } 313 } 314 315 // a bit from the Aenid 316 public static final String[] WORDS = new String[] 317 { 318 "ipsa", "iovis", "rapidum", "iaculata", "e", "nubibus", "ignem", 319 "disiecitque", "rates", "evertitque", "aequora", "ventis", 320 "illum", "exspirantem", "transfixo", "pectore", "flammas", 321 "turbine", "corripuit", "scopuloque", "infixit", "acuto", 322 "ast", "ego", "quae", "divum", "incedo", "regina", "iovisque", 323 "et", "soror", "et", "coniunx", "una", "cum", "gente", "tot", "annos", 324 "bella", "gero", "et", "quisquam", "numen", "iunonis", "adorat", 325 "praeterea", "aut", "supplex", "aris", "imponet", "honorem", 326 "talia", "flammato", "secum", "dea", "corde", "volutans", 327 "nimborum", "in", "patriam", "loca", "feta", "furentibus", "austris", 328 "aeoliam", "venit", "hic", "vasto", "rex", "aeolus", "antro", 329 "luctantis", "ventos", "tempestatesque", "sonoras", 330 "imperio", "premit", "ac", "vinclis", "et", "carcere", "frenat", 331 "illi", "indignantes", "magno", "cum", "murmure", "montis", 332 "circum", "claustra", "fremunt", "celsa", "sedet", "aeolus", "arce", 333 "sceptra", "tenens", "mollitque", "animos", "et", "temperat", "iras", 334 "ni", "faciat", "maria", "ac", "terras", "caelumque", "profundum", 335 "quippe", "ferant", "rapidi", "secum", "verrantque", "per", "auras", 336 "sed", "pater", "omnipotens", "speluncis", "abdidit", "atris", 337 "hoc", "metuens", "molemque", "et", "montis", "insuper", "altos", 338 "imposuit", "regemque", "dedit", "qui", "foedere", "certo", 339 "et", "premere", "et", "laxas", "sciret", "dare", "iussus", "habenas", 340 }; 341 342 343 344 private static final String[] DNS1 = new String[] { "corp", "your", "my", "sample", "company", "test", "any" }; 345 private static final String[] DNS2 = new String[] { "com", "org", "com", "gov", "org", "com", "org", "com", "edu" }; 346 347 private int pick(int n) 348 { 349 return _picker.nextInt(n); 350 } 351 352 private String pick(String[] a) 353 { 354 return a[pick(a.length)]; 355 } 356 357 private String pick(String[] a, int count) 358 { 359 if (count <= 0) 360 return ""; 361 362 int i = pick(a.length); 363 StringBuffer sb = new StringBuffer(a[i]); 364 while (count-- > 0) 365 { 366 i += 1; 367 if (i >= a.length) 368 i = 0; 369 sb.append(' '); 370 sb.append(a[i]); 371 } 372 return sb.toString(); 373 } 374 375 private String pickDigits(int digits) 376 { 377 StringBuffer sb = new StringBuffer(); 378 while (digits-- > 0) 379 sb.append(Integer.toString(pick(10))); 380 return sb.toString(); 381 } 382 383 private int pickLength(SchemaType sType) 384 { 385 XmlInteger length = (XmlInteger) sType.getFacet(SchemaType.FACET_LENGTH); 386 if (length != null) 387 return length.getBigIntegerValue().intValue(); 388 XmlInteger min = (XmlInteger) sType.getFacet(SchemaType.FACET_MIN_LENGTH); 389 XmlInteger max = (XmlInteger) sType.getFacet(SchemaType.FACET_MAX_LENGTH); 390 int minInt, maxInt; 391 if (min == null) 392 minInt = 0; 393 else 394 minInt = min.getBigIntegerValue().intValue(); 395 if (max == null) 396 maxInt = Integer.MAX_VALUE; 397 else 398 maxInt = max.getBigIntegerValue().intValue(); 399 // We try to keep the length of the array within reasonable limits, 400 // at least 1 item and at most 3 if possible 401 if (minInt == 0 && maxInt >= 1) 402 minInt = 1; 403 if (maxInt > minInt + 2) 404 maxInt = minInt + 2; 405 if (maxInt < minInt) 406 maxInt = minInt; 407 return minInt + pick(maxInt-minInt); 408 } 409 410 /** 411 * Formats a given string to the required length, using the following operations: 412 * - append the source string to itself as necessary to pass the minLength; 413 * - truncate the result of previous step, if necessary, to keep it within minLength. 414 */ 415 private String formatToLength(String s, SchemaType sType) 416 { 417 String result = s; 418 try 419 { 420 SimpleValue min = (SimpleValue)sType.getFacet(SchemaType.FACET_LENGTH); 421 if (min == null) 422 min = (SimpleValue)sType.getFacet(SchemaType.FACET_MIN_LENGTH); 423 if (min != null) 424 { 425 int len = min.getIntValue(); 426 while (result.length() < len) 427 result = result + result; 428 } 429 SimpleValue max = (SimpleValue)sType.getFacet(SchemaType.FACET_LENGTH); 430 if (max == null) 431 max = (SimpleValue)sType.getFacet(SchemaType.FACET_MAX_LENGTH); 432 if (max != null) 433 { 434 int len = max.getIntValue(); 435 if (result.length() > len) 436 result = result.substring(0, len); 437 } 438 } 439 catch (Exception e) // intValue can be out of range 440 { 441 } 442 return result; 443 } 444 445 private String formatDecimal(String start, SchemaType sType) 446 { 447 BigDecimal result = new BigDecimal(start); 448 XmlDecimal xmlD; 449 xmlD = (XmlDecimal) sType.getFacet(SchemaType.FACET_MIN_INCLUSIVE); 450 BigDecimal min = xmlD != null ? xmlD.getBigDecimalValue() : null; 451 xmlD = (XmlDecimal) sType.getFacet(SchemaType.FACET_MAX_INCLUSIVE); 452 BigDecimal max = xmlD != null ? xmlD.getBigDecimalValue() : null; 453 boolean minInclusive = true, maxInclusive = true; 454 xmlD = (XmlDecimal) sType.getFacet(SchemaType.FACET_MIN_EXCLUSIVE); 455 if (xmlD != null) 456 { 457 BigDecimal minExcl = xmlD.getBigDecimalValue(); 458 if (min == null || min.compareTo(minExcl) < 0) 459 { 460 min = minExcl; 461 minInclusive = false; 462 } 463 } 464 xmlD = (XmlDecimal) sType.getFacet(SchemaType.FACET_MAX_EXCLUSIVE); 465 if (xmlD != null) 466 { 467 BigDecimal maxExcl = xmlD.getBigDecimalValue(); 468 if (max == null || max.compareTo(maxExcl) > 0) 469 { 470 max = maxExcl; 471 maxInclusive = false; 472 } 473 } 474 xmlD = (XmlDecimal) sType.getFacet(SchemaType.FACET_TOTAL_DIGITS); 475 int totalDigits = -1; 476 if (xmlD != null) 477 { 478 totalDigits = xmlD.getBigDecimalValue().intValue(); 479 480 StringBuffer sb = new StringBuffer(totalDigits); 481 for (int i = 0; i < totalDigits; i++) 482 sb.append('9'); 483 BigDecimal digitsLimit = new BigDecimal(sb.toString()); 484 if (max != null && max.compareTo(digitsLimit) > 0) 485 { 486 max = digitsLimit; 487 maxInclusive = true; 488 } 489 digitsLimit = digitsLimit.negate(); 490 if (min != null && min.compareTo(digitsLimit) < 0) 491 { 492 min = digitsLimit; 493 minInclusive = true; 494 } 495 } 496 497 int sigMin = min == null ? 1 : result.compareTo(min); 498 int sigMax = max == null ? -1 : result.compareTo(max); 499 boolean minOk = sigMin > 0 || sigMin == 0 && minInclusive; 500 boolean maxOk = sigMax < 0 || sigMax == 0 && maxInclusive; 501 502 // Compute the minimum increment 503 xmlD = (XmlDecimal) sType.getFacet(SchemaType.FACET_FRACTION_DIGITS); 504 int fractionDigits = -1; 505 BigDecimal increment; 506 if (xmlD == null) 507 increment = new BigDecimal(1); 508 else 509 { 510 fractionDigits = xmlD.getBigDecimalValue().intValue(); 511 if (fractionDigits > 0) 512 { 513 StringBuffer sb = new StringBuffer("0."); 514 for (int i = 1; i < fractionDigits; i++) 515 sb.append('0'); 516 sb.append('1'); 517 increment = new BigDecimal(sb.toString()); 518 } 519 else 520 increment = new BigDecimal(1.0); 521 } 522 523 if (minOk && maxOk) 524 { 525 // OK 526 } 527 else if (minOk && !maxOk) 528 { 529 // TOO BIG 530 if (maxInclusive) 531 result = max; 532 else 533 result = max.subtract(increment); 534 } 535 else if (!minOk && maxOk) 536 { 537 // TOO SMALL 538 if (minInclusive) 539 result = min; 540 else 541 result = min.add(increment); 542 } 543 else 544 { 545 // MIN > MAX!! 546 } 547 548 // We have the number 549 // Adjust the scale according to the totalDigits and fractionDigits 550 int digits = 0; 551 BigDecimal ONE = new BigDecimal(BigInteger.ONE); 552 for (BigDecimal n = result; n.abs().compareTo(ONE) >= 0; digits++) 553 n = n.movePointLeft(1); 554 555 if (fractionDigits > 0) 556 if (totalDigits >= 0) 557 result = result.setScale(Math.max(fractionDigits, totalDigits - digits)); 558 else 559 result = result.setScale(fractionDigits); 560 else if (fractionDigits == 0) 561 result = result.setScale(0); 562 563 return result.toString(); 564 } 565 566 private String formatDuration(SchemaType sType) 567 { 568 XmlDuration d = 569 (XmlDuration) sType.getFacet(SchemaType.FACET_MIN_INCLUSIVE); 570 GDuration minInclusive = null; 571 if (d != null) 572 minInclusive = d.getGDurationValue(); 573 574 d = (XmlDuration) sType.getFacet(SchemaType.FACET_MAX_INCLUSIVE); 575 GDuration maxInclusive = null; 576 if (d != null) 577 maxInclusive = d.getGDurationValue(); 578 579 d = (XmlDuration) sType.getFacet(SchemaType.FACET_MIN_EXCLUSIVE); 580 GDuration minExclusive = null; 581 if (d != null) 582 minExclusive = d.getGDurationValue(); 583 584 d = (XmlDuration) sType.getFacet(SchemaType.FACET_MAX_EXCLUSIVE); 585 GDuration maxExclusive = null; 586 if (d != null) 587 maxExclusive = d.getGDurationValue(); 588 589 GDurationBuilder gdurb = new GDurationBuilder(); 590 BigInteger min, max; 591 592 gdurb.setSecond(pick(800000)); 593 gdurb.setMonth(pick(20)); 594 595 // Years 596 // Months 597 // Days 598 // Hours 599 // Minutes 600 // Seconds 601 // Fractions 602 if (minInclusive != null) 603 { 604 if (gdurb.getYear() < minInclusive.getYear()) 605 gdurb.setYear(minInclusive.getYear()); 606 if (gdurb.getMonth() < minInclusive.getMonth()) 607 gdurb.setMonth(minInclusive.getMonth()); 608 if (gdurb.getDay() < minInclusive.getDay()) 609 gdurb.setDay(minInclusive.getDay()); 610 if (gdurb.getHour() < minInclusive.getHour()) 611 gdurb.setHour(minInclusive.getHour()); 612 if (gdurb.getMinute() < minInclusive.getMinute()) 613 gdurb.setMinute(minInclusive.getMinute()); 614 if (gdurb.getSecond() < minInclusive.getSecond()) 615 gdurb.setSecond(minInclusive.getSecond()); 616 if (gdurb.getFraction().compareTo(minInclusive.getFraction()) < 0) 617 gdurb.setFraction(minInclusive.getFraction()); 618 } 619 620 if (maxInclusive != null) 621 { 622 if (gdurb.getYear() > maxInclusive.getYear()) 623 gdurb.setYear(maxInclusive.getYear()); 624 if (gdurb.getMonth() > maxInclusive.getMonth()) 625 gdurb.setMonth(maxInclusive.getMonth()); 626 if (gdurb.getDay() > maxInclusive.getDay()) 627 gdurb.setDay(maxInclusive.getDay()); 628 if (gdurb.getHour() > maxInclusive.getHour()) 629 gdurb.setHour(maxInclusive.getHour()); 630 if (gdurb.getMinute() > maxInclusive.getMinute()) 631 gdurb.setMinute(maxInclusive.getMinute()); 632 if (gdurb.getSecond() > maxInclusive.getSecond()) 633 gdurb.setSecond(maxInclusive.getSecond()); 634 if (gdurb.getFraction().compareTo(maxInclusive.getFraction()) > 0) 635 gdurb.setFraction(maxInclusive.getFraction()); 636 } 637 638 if (minExclusive != null) 639 { 640 if (gdurb.getYear() <= minExclusive.getYear()) 641 gdurb.setYear(minExclusive.getYear()+1); 642 if (gdurb.getMonth() <= minExclusive.getMonth()) 643 gdurb.setMonth(minExclusive.getMonth()+1); 644 if (gdurb.getDay() <= minExclusive.getDay()) 645 gdurb.setDay(minExclusive.getDay()+1); 646 if (gdurb.getHour() <= minExclusive.getHour()) 647 gdurb.setHour(minExclusive.getHour()+1); 648 if (gdurb.getMinute() <= minExclusive.getMinute()) 649 gdurb.setMinute(minExclusive.getMinute()+1); 650 if (gdurb.getSecond() <= minExclusive.getSecond()) 651 gdurb.setSecond(minExclusive.getSecond()+1); 652 if (gdurb.getFraction().compareTo(minExclusive.getFraction()) <= 0) 653 gdurb.setFraction(minExclusive.getFraction().add(new BigDecimal(0.001))); 654 } 655 656 if (maxExclusive != null) 657 { 658 if (gdurb.getYear() > maxExclusive.getYear()) 659 gdurb.setYear(maxExclusive.getYear()); 660 if (gdurb.getMonth() > maxExclusive.getMonth()) 661 gdurb.setMonth(maxExclusive.getMonth()); 662 if (gdurb.getDay() > maxExclusive.getDay()) 663 gdurb.setDay(maxExclusive.getDay()); 664 if (gdurb.getHour() > maxExclusive.getHour()) 665 gdurb.setHour(maxExclusive.getHour()); 666 if (gdurb.getMinute() > maxExclusive.getMinute()) 667 gdurb.setMinute(maxExclusive.getMinute()); 668 if (gdurb.getSecond() > maxExclusive.getSecond()) 669 gdurb.setSecond(maxExclusive.getSecond()); 670 if (gdurb.getFraction().compareTo(maxExclusive.getFraction()) > 0) 671 gdurb.setFraction(maxExclusive.getFraction()); 672 } 673 674 gdurb.normalize(); 675 return gdurb.toString(); 676 } 677 678 private String formatDate(SchemaType sType) 679 { 680 GDateBuilder gdateb = new GDateBuilder(new Date(1000L * pick(365 * 24 * 60 * 60) + (30L + pick(20)) * 365 * 24 * 60 * 60 * 1000)); 681 GDate min = null, max = null; 682 GDate temp; 683 684 // Find the min and the max according to the type 685 switch (sType.getPrimitiveType().getBuiltinTypeCode()) 686 { 687 case SchemaType.BTC_DATE_TIME: 688 { 689 XmlDateTime x = (XmlDateTime) sType.getFacet(SchemaType.FACET_MIN_INCLUSIVE); 690 if (x != null) 691 min = x.getGDateValue(); 692 x = (XmlDateTime) sType.getFacet(SchemaType.FACET_MIN_EXCLUSIVE); 693 if (x != null) 694 if (min == null || min.compareToGDate(x.getGDateValue()) <= 0) 695 min = x.getGDateValue(); 696 697 x = (XmlDateTime) sType.getFacet(SchemaType.FACET_MAX_INCLUSIVE); 698 if (x != null) 699 max = x.getGDateValue(); 700 x = (XmlDateTime) sType.getFacet(SchemaType.FACET_MAX_EXCLUSIVE); 701 if (x != null) 702 if (max == null || max.compareToGDate(x.getGDateValue()) >= 0) 703 max = x.getGDateValue(); 704 break; 705 } 706 case SchemaType.BTC_TIME: 707 { 708 XmlTime x = (XmlTime) sType.getFacet(SchemaType.FACET_MIN_INCLUSIVE); 709 if (x != null) 710 min = x.getGDateValue(); 711 x = (XmlTime) sType.getFacet(SchemaType.FACET_MIN_EXCLUSIVE); 712 if (x != null) 713 if (min == null || min.compareToGDate(x.getGDateValue()) <= 0) 714 min = x.getGDateValue(); 715 716 x = (XmlTime) sType.getFacet(SchemaType.FACET_MAX_INCLUSIVE); 717 if (x != null) 718 max = x.getGDateValue(); 719 x = (XmlTime) sType.getFacet(SchemaType.FACET_MAX_EXCLUSIVE); 720 if (x != null) 721 if (max == null || max.compareToGDate(x.getGDateValue()) >= 0) 722 max = x.getGDateValue(); 723 break; 724 } 725 case SchemaType.BTC_DATE: 726 { 727 XmlDate x = (XmlDate) sType.getFacet(SchemaType.FACET_MIN_INCLUSIVE); 728 if (x != null) 729 min = x.getGDateValue(); 730 x = (XmlDate) sType.getFacet(SchemaType.FACET_MIN_EXCLUSIVE); 731 if (x != null) 732 if (min == null || min.compareToGDate(x.getGDateValue()) <= 0) 733 min = x.getGDateValue(); 734 735 x = (XmlDate) sType.getFacet(SchemaType.FACET_MAX_INCLUSIVE); 736 if (x != null) 737 max = x.getGDateValue(); 738 x = (XmlDate) sType.getFacet(SchemaType.FACET_MAX_EXCLUSIVE); 739 if (x != null) 740 if (max == null || max.compareToGDate(x.getGDateValue()) >= 0) 741 max = x.getGDateValue(); 742 break; 743 } 744 case SchemaType.BTC_G_YEAR_MONTH: 745 { 746 XmlGYearMonth x = (XmlGYearMonth) sType.getFacet(SchemaType.FACET_MIN_INCLUSIVE); 747 if (x != null) 748 min = x.getGDateValue(); 749 x = (XmlGYearMonth) sType.getFacet(SchemaType.FACET_MIN_EXCLUSIVE); 750 if (x != null) 751 if (min == null || min.compareToGDate(x.getGDateValue()) <= 0) 752 min = x.getGDateValue(); 753 754 x = (XmlGYearMonth) sType.getFacet(SchemaType.FACET_MAX_INCLUSIVE); 755 if (x != null) 756 max = x.getGDateValue(); 757 x = (XmlGYearMonth) sType.getFacet(SchemaType.FACET_MAX_EXCLUSIVE); 758 if (x != null) 759 if (max == null || max.compareToGDate(x.getGDateValue()) >= 0) 760 max = x.getGDateValue(); 761 break; 762 } 763 case SchemaType.BTC_G_YEAR: 764 { 765 XmlGYear x = (XmlGYear) sType.getFacet(SchemaType.FACET_MIN_INCLUSIVE); 766 if (x != null) 767 min = x.getGDateValue(); 768 x = (XmlGYear) sType.getFacet(SchemaType.FACET_MIN_EXCLUSIVE); 769 if (x != null) 770 if (min == null || min.compareToGDate(x.getGDateValue()) <= 0) 771 min = x.getGDateValue(); 772 773 x = (XmlGYear) sType.getFacet(SchemaType.FACET_MAX_INCLUSIVE); 774 if (x != null) 775 max = x.getGDateValue(); 776 x = (XmlGYear) sType.getFacet(SchemaType.FACET_MAX_EXCLUSIVE); 777 if (x != null) 778 if (max == null || max.compareToGDate(x.getGDateValue()) >= 0) 779 max = x.getGDateValue(); 780 break; 781 } 782 case SchemaType.BTC_G_MONTH_DAY: 783 { 784 XmlGMonthDay x = (XmlGMonthDay) sType.getFacet(SchemaType.FACET_MIN_INCLUSIVE); 785 if (x != null) 786 min = x.getGDateValue(); 787 x = (XmlGMonthDay) sType.getFacet(SchemaType.FACET_MIN_EXCLUSIVE); 788 if (x != null) 789 if (min == null || min.compareToGDate(x.getGDateValue()) <= 0) 790 min = x.getGDateValue(); 791 792 x = (XmlGMonthDay) sType.getFacet(SchemaType.FACET_MAX_INCLUSIVE); 793 if (x != null) 794 max = x.getGDateValue(); 795 x = (XmlGMonthDay) sType.getFacet(SchemaType.FACET_MAX_EXCLUSIVE); 796 if (x != null) 797 if (max == null || max.compareToGDate(x.getGDateValue()) >= 0) 798 max = x.getGDateValue(); 799 break; 800 } 801 case SchemaType.BTC_G_DAY: 802 { 803 XmlGDay x = (XmlGDay) sType.getFacet(SchemaType.FACET_MIN_INCLUSIVE); 804 if (x != null) 805 min = x.getGDateValue(); 806 x = (XmlGDay) sType.getFacet(SchemaType.FACET_MIN_EXCLUSIVE); 807 if (x != null) 808 if (min == null || min.compareToGDate(x.getGDateValue()) <= 0) 809 min = x.getGDateValue(); 810 811 x = (XmlGDay) sType.getFacet(SchemaType.FACET_MAX_INCLUSIVE); 812 if (x != null) 813 max = x.getGDateValue(); 814 x = (XmlGDay) sType.getFacet(SchemaType.FACET_MAX_EXCLUSIVE); 815 if (x != null) 816 if (max == null || max.compareToGDate(x.getGDateValue()) >= 0) 817 max = x.getGDateValue(); 818 break; 819 } 820 case SchemaType.BTC_G_MONTH: 821 { 822 XmlGMonth x = (XmlGMonth) sType.getFacet(SchemaType.FACET_MIN_INCLUSIVE); 823 if (x != null) 824 min = x.getGDateValue(); 825 x = (XmlGMonth) sType.getFacet(SchemaType.FACET_MIN_EXCLUSIVE); 826 if (x != null) 827 if (min == null || min.compareToGDate(x.getGDateValue()) <= 0) 828 min = x.getGDateValue(); 829 830 x = (XmlGMonth) sType.getFacet(SchemaType.FACET_MAX_INCLUSIVE); 831 if (x != null) 832 max = x.getGDateValue(); 833 x = (XmlGMonth) sType.getFacet(SchemaType.FACET_MAX_EXCLUSIVE); 834 if (x != null) 835 if (max == null || max.compareToGDate(x.getGDateValue()) >= 0) 836 max = x.getGDateValue(); 837 break; 838 } 839 } 840 841 if (min != null && max == null) 842 { 843 if (min.compareToGDate(gdateb) >= 0) 844 { 845 // Reset the date to min + (1-8) hours 846 Calendar c = gdateb.getCalendar(); 847 c.add(Calendar.HOUR_OF_DAY, pick(8)); 848 gdateb = new GDateBuilder(c); 849 } 850 } 851 else if (min == null && max != null) 852 { 853 if (max.compareToGDate(gdateb) <= 0) 854 { 855 // Reset the date to max - (1-8) hours 856 Calendar c = gdateb.getCalendar(); 857 c.add(Calendar.HOUR_OF_DAY, 0-pick(8)); 858 gdateb = new GDateBuilder(c); 859 } 860 } 861 else if (min != null && max != null) 862 { 863 if (min.compareToGDate(gdateb) >= 0 || max.compareToGDate(gdateb) <= 0) 864 { 865 // Find a date between the two 866 Calendar c = min.getCalendar(); 867 Calendar cmax = max.getCalendar(); 868 c.add(Calendar.HOUR_OF_DAY, 1); 869 if (c.after(cmax)) 870 { 871 c.add(Calendar.HOUR_OF_DAY, -1); 872 c.add(Calendar.MINUTE, 1); 873 if (c.after(cmax)) 874 { 875 c.add(Calendar.MINUTE, -1); 876 c.add(Calendar.SECOND, 1); 877 if (c.after(cmax)) 878 { 879 c.add(Calendar.SECOND, -1); 880 c.add(Calendar.MILLISECOND, 1); 881 if (c.after(cmax)) 882 c.add(Calendar.MILLISECOND, -1); 883 } 884 } 885 } 886 gdateb = new GDateBuilder(c); 887 } 888 } 889 890 gdateb.setBuiltinTypeCode(sType.getPrimitiveType().getBuiltinTypeCode()); 891 if (pick(2) == 0) 892 gdateb.clearTimeZone(); 893 return gdateb.toString(); 894 } 895 896 private SchemaType closestBuiltin(SchemaType sType) 897 { 898 while (!sType.isBuiltinType()) 899 sType = sType.getBaseType(); 900 return sType; 901 } 902 903 /** 904 * Cracks a combined QName of the form URL:localname 905 */ 906 public static QName crackQName(String qName) 907 { 908 String ns; 909 String name; 910 911 int index = qName.lastIndexOf( ':' ); 912 if (index >= 0) 913 { 914 ns = qName.substring( 0, index ); 915 name = qName.substring( index + 1); 916 } 917 else 918 { 919 ns = ""; 920 name = qName; 921 } 922 923 return new QName(ns, name); 924 } 925 926 927 /** 928 * Cursor position: 929 * Before this call: 930 * <outer><foo/>^</outer> (cursor at the ^) 931 * After this call: 932 * <<outer><foo/><bar/>som text<etc/>^</outer> 933 */ 934 private void processParticle(SchemaParticle sp, XmlCursor xmlc, boolean mixed) 935 { 936 int loop = determineMinMaxForSample(sp, xmlc); 937 938 while (loop-- > 0) 939 { 940 switch (sp.getParticleType()) 941 { 942 case (SchemaParticle.ELEMENT) : 943 processElement(sp, xmlc, mixed); 944 break; 945 case (SchemaParticle.SEQUENCE) : 946 processSequence(sp, xmlc, mixed); 947 break; 948 case (SchemaParticle.CHOICE) : 949 processChoice(sp, xmlc, mixed); 950 break; 951 case (SchemaParticle.ALL) : 952 processAll(sp, xmlc, mixed); 953 break; 954 case (SchemaParticle.WILDCARD) : 955 processWildCard(sp, xmlc, mixed); 956 break; 957 default : 958 // throw new Exception("No Match on Schema Particle Type: " + String.valueOf(sp.getParticleType())); 959 } 960 } 961 } 962 963 private int determineMinMaxForSample(SchemaParticle sp, XmlCursor xmlc) 964 { 965 int minOccurs = sp.getIntMinOccurs(); 966 int maxOccurs = sp.getIntMaxOccurs(); 967 968 if (minOccurs == maxOccurs) 969 return minOccurs; 970 971 int result = minOccurs; 972 if (result == 0 && _nElements < MAX_ELEMENTS) 973 result = 1; 974 975 if (sp.getParticleType() != SchemaParticle.ELEMENT) 976 return result; 977 978 // it probably only makes sense to put comments in front of individual elements that repeat 979 980 if (sp.getMaxOccurs() == null) 981 { 982 // xmlc.insertComment("The next " + getItemNameOrType(sp, xmlc) + " may be repeated " + minOccurs + " or more times"); 983 if (minOccurs == 0) 984 xmlc.insertComment("Zero or more repetitions:"); 985 else 986 xmlc.insertComment(minOccurs + " or more repetitions:"); 987 } 988 else if (sp.getIntMaxOccurs() > 1) 989 { 990 xmlc.insertComment(minOccurs + " to " + String.valueOf(sp.getMaxOccurs()) + " repetitions:"); 991 } 992 else 993 { 994 xmlc.insertComment("Optional:"); 995 } 996 return result; 997 } 998 999 /* 1000 Return a name for the element or the particle type to use in the comment for minoccurs, max occurs 1001 */ 1002 private String getItemNameOrType(SchemaParticle sp, XmlCursor xmlc) 1003 { 1004 String elementOrTypeName = null; 1005 if (sp.getParticleType() == SchemaParticle.ELEMENT) 1006 { 1007 elementOrTypeName = "Element (" + sp.getName().getLocalPart() + ")"; 1008 } 1009 else 1010 { 1011 elementOrTypeName = printParticleType(sp.getParticleType()); 1012 } 1013 return elementOrTypeName; 1014 } 1015 1016 private void processElement(SchemaParticle sp, XmlCursor xmlc, boolean mixed) 1017 { 1018 // cast as schema local element 1019 SchemaLocalElement element = (SchemaLocalElement) sp; 1020 /// ^ -> <elemenname></elem>^ 1021 if (_soapEnc) 1022 xmlc.insertElement(element.getName().getLocalPart()); // soap encoded? drop namespaces. 1023 else 1024 xmlc.insertElement(element.getName().getLocalPart(), element.getName().getNamespaceURI()); 1025 _nElements++; 1026 /// -> <elem>^</elem> 1027 xmlc.toPrevToken(); 1028 // -> <elem>stuff^</elem> 1029 1030 createSampleForType(element.getType(), xmlc); 1031 // -> <elem>stuff</elem>^ 1032 xmlc.toNextToken(); 1033 1034 } 1035 1036 private void moveToken(int numToMove, XmlCursor xmlc) 1037 { 1038 for (int i = 0; i < Math.abs(numToMove); i++) 1039 { 1040 if (numToMove < 0) 1041 { 1042 xmlc.toPrevToken(); 1043 } 1044 else 1045 { 1046 xmlc.toNextToken(); 1047 } 1048 } 1049 } 1050 1051 private static final String formatQName(XmlCursor xmlc, QName qName) 1052 { 1053 XmlCursor parent = xmlc.newCursor(); 1054 parent.toParent(); 1055 String prefix = parent.prefixForNamespace(qName.getNamespaceURI()); 1056 parent.dispose(); 1057 String name; 1058 if (prefix == null || prefix.length() == 0) 1059 name = qName.getLocalPart(); 1060 else 1061 name = prefix + ":" + qName.getLocalPart(); 1062 return name; 1063 } 1064 1065 private static final QName HREF = new QName("href"); 1066 private static final QName ID = new QName("id"); 1067 private static final QName XSI_TYPE = new QName("http://www.w3.org/2001/XMLSchema-instance", "type"); 1068 private static final QName ENC_ARRAYTYPE = new QName("http://schemas.xmlsoap.org/soap/encoding/", "arrayType"); 1069 private static final QName ENC_OFFSET = new QName("http://schemas.xmlsoap.org/soap/encoding/", "offset"); 1070 1071 private static final Set SKIPPED_SOAP_ATTRS = new HashSet(Arrays.asList(new QName[] { HREF, ID, ENC_OFFSET})); 1072 private void processAttributes(SchemaType stype, XmlCursor xmlc) 1073 { 1074 if (_soapEnc) 1075 { 1076 QName typeName = stype.getName(); 1077 if (typeName != null) 1078 { 1079 xmlc.insertAttributeWithValue(XSI_TYPE, formatQName(xmlc, typeName)); 1080 } 1081 } 1082 1083 SchemaProperty[] attrProps = stype.getAttributeProperties(); 1084 for (int i = 0; i < attrProps.length; i++) 1085 { 1086 SchemaProperty attr = attrProps[i]; 1087 if (_soapEnc) 1088 { 1089 if (SKIPPED_SOAP_ATTRS.contains(attr.getName())) 1090 continue; 1091 if (ENC_ARRAYTYPE.equals(attr.getName())) 1092 { 1093 SOAPArrayType arrayType = ((SchemaWSDLArrayType)stype.getAttributeModel().getAttribute(attr.getName())).getWSDLArrayType(); 1094 if (arrayType != null) 1095 xmlc.insertAttributeWithValue(attr.getName(), formatQName(xmlc, arrayType.getQName()) + arrayType.soap11DimensionString()); 1096 continue; 1097 } 1098 } 1099 String defaultValue = attr.getDefaultText(); 1100 xmlc.insertAttributeWithValue(attr.getName(), defaultValue == null ? 1101 sampleDataForSimpleType(attr.getType()) : defaultValue); 1102 } 1103 } 1104 1105 private void processSequence(SchemaParticle sp, XmlCursor xmlc, boolean mixed) 1106 { 1107 SchemaParticle[] spc = sp.getParticleChildren(); 1108 for (int i=0; i < spc.length; i++) 1109 { 1110 /// <parent>maybestuff^</parent> 1111 processParticle(spc[i], xmlc, mixed); 1112 //<parent>maybestuff...morestuff^</parent> 1113 if (mixed && i < spc.length-1) 1114 xmlc.insertChars(pick(WORDS)); 1115 } 1116 } 1117 1118 private void processChoice(SchemaParticle sp, XmlCursor xmlc, boolean mixed) 1119 { 1120 SchemaParticle[] spc = sp.getParticleChildren(); 1121 xmlc.insertComment("You have a CHOICE of the next " + String.valueOf(spc.length) + " items at this level"); 1122 for (int i=0; i < spc.length; i++) 1123 { 1124 processParticle(spc[i], xmlc, mixed); 1125 } 1126 } 1127 1128 private void processAll(SchemaParticle sp, XmlCursor xmlc, boolean mixed) 1129 { 1130 SchemaParticle[] spc = sp.getParticleChildren(); 1131 // xmlc.insertComment("You may enter the following " + String.valueOf(spc.length) + " items in any order"); 1132 for (int i=0; i < spc.length; i++) 1133 { 1134 processParticle(spc[i], xmlc, mixed); 1135 if (mixed && i < spc.length-1) 1136 xmlc.insertChars(pick(WORDS)); 1137 } 1138 } 1139 1140 private void processWildCard(SchemaParticle sp, XmlCursor xmlc, boolean mixed) 1141 { 1142 xmlc.insertComment("You may enter ANY elements at this point"); 1143 xmlc.insertElement("AnyElement"); 1144 } 1145 1146 /** 1147 * This method will get the base type for the schema type 1148 */ 1149 1150 private static QName getClosestName(SchemaType sType) 1151 { 1152 while (sType.getName() == null) 1153 sType = sType.getBaseType(); 1154 1155 return sType.getName(); 1156 } 1157 1158 private String printParticleType(int particleType) 1159 { 1160 StringBuffer returnParticleType = new StringBuffer(); 1161 returnParticleType.append("Schema Particle Type: "); 1162 1163 switch (particleType) 1164 { 1165 case SchemaParticle.ALL : 1166 returnParticleType.append("ALL\n"); 1167 break; 1168 case SchemaParticle.CHOICE : 1169 returnParticleType.append("CHOICE\n"); 1170 break; 1171 case SchemaParticle.ELEMENT : 1172 returnParticleType.append("ELEMENT\n"); 1173 break; 1174 case SchemaParticle.SEQUENCE : 1175 returnParticleType.append("SEQUENCE\n"); 1176 break; 1177 case SchemaParticle.WILDCARD : 1178 returnParticleType.append("WILDCARD\n"); 1179 break; 1180 default : 1181 returnParticleType.append("Schema Particle Type Unknown"); 1182 break; 1183 } 1184 1185 return returnParticleType.toString(); 1186 } 1187 1188 private ArrayList _typeStack = new ArrayList(); 1189 }