1 16 17 package org.cojen.util; 18 19 import java.io.Serializable ; 20 import java.util.ArrayList ; 21 import java.util.Comparator ; 22 import java.util.List ; 23 import java.util.Map ; 24 import java.lang.reflect.Constructor ; 25 import java.lang.reflect.Method ; 26 import java.lang.reflect.InvocationTargetException ; 27 import org.cojen.classfile.ClassFile; 28 import org.cojen.classfile.CodeBuilder; 29 import org.cojen.classfile.Label; 30 import org.cojen.classfile.LocalVariable; 31 import org.cojen.classfile.MethodInfo; 32 import org.cojen.classfile.Modifiers; 33 import org.cojen.classfile.Opcode; 34 import org.cojen.classfile.TypeDesc; 35 36 92 public class BeanComparator implements Comparator , Serializable { 93 private static Map cGeneratedComparatorCache; 95 96 static { 97 cGeneratedComparatorCache = new SoftValuedHashMap(); 98 } 99 100 107 public static BeanComparator forClass(Class clazz) { 108 return new BeanComparator(clazz); 109 } 110 111 114 private static boolean equalTest(Object obj1, Object obj2) { 115 return (obj1 == obj2) ? true : 116 ((obj1 == null || obj2 == null) ? false : obj1.equals(obj2)); 117 } 118 119 122 129 130 private Class mBeanClass; 131 132 private transient Map mProperties; 134 135 private String mOrderByName; 136 137 private Comparator mUsingComparator; 138 139 private int mFlags; 143 144 private Comparator mCollator; 146 147 private BeanComparator mParent; 148 149 private transient Comparator mComparator; 151 152 private transient boolean mHasHashCode; 153 private transient int mHashCode; 154 155 private BeanComparator(Class clazz) { 156 mBeanClass = clazz; 157 mCollator = String.CASE_INSENSITIVE_ORDER; 158 } 159 160 private BeanComparator(BeanComparator parent) { 161 mParent = parent; 162 mBeanClass = parent.mBeanClass; 163 mProperties = parent.getProperties(); 164 mCollator = parent.mCollator; 165 } 166 167 199 public BeanComparator orderBy(String propertyName) 200 throws IllegalArgumentException 201 { 202 int dot = propertyName.indexOf('.'); 203 String subName; 204 if (dot < 0) { 205 subName = null; 206 } else { 207 subName = propertyName.substring(dot + 1); 208 propertyName = propertyName.substring(0, dot); 209 } 210 211 boolean reverse = false; 212 if (propertyName.length() > 0) { 213 char prefix = propertyName.charAt(0); 214 switch (prefix) { 215 default: 216 break; 217 case '-': 218 reverse = true; 219 case '+': 221 propertyName = propertyName.substring(1); 222 } 223 } 224 225 BeanProperty prop = (BeanProperty)getProperties().get(propertyName); 226 227 if (prop == null) { 228 throw new IllegalArgumentException 229 ("Property '" + propertyName + "' doesn't exist in '" + 230 mBeanClass.getName() + '\''); 231 } 232 233 if (prop.getReadMethod() == null) { 234 throw new IllegalArgumentException 235 ("Property '" + propertyName + "' cannot be read"); 236 } 237 238 if (propertyName.equals(mOrderByName)) { 239 propertyName = new String (propertyName); 244 } 245 246 BeanComparator bc = new BeanComparator(this); 247 bc.mOrderByName = propertyName; 248 249 if (subName != null) { 250 BeanComparator subOrder = forClass(prop.getType()); 251 subOrder.mCollator = mCollator; 252 bc = bc.using(subOrder.orderBy(subName)); 253 } 254 255 return reverse ? bc.reverse() : bc; 256 } 257 258 273 public BeanComparator using(Comparator c) { 274 BeanComparator bc = new BeanComparator(this); 275 bc.mOrderByName = mOrderByName; 276 bc.mUsingComparator = c; 277 bc.mFlags = mFlags; 278 return bc; 279 } 280 281 286 public BeanComparator reverse() { 287 BeanComparator bc = new BeanComparator(this); 288 bc.mOrderByName = mOrderByName; 289 bc.mUsingComparator = mUsingComparator; 290 bc.mFlags = mFlags ^ 0x01; 291 return bc; 292 } 293 294 304 public BeanComparator nullHigh() { 305 BeanComparator bc = new BeanComparator(this); 306 bc.mOrderByName = mOrderByName; 307 bc.mUsingComparator = mUsingComparator; 308 bc.mFlags = mFlags ^ ((mFlags & 0x01) << 1); 309 return bc; 310 } 311 312 320 public BeanComparator nullLow() { 321 BeanComparator bc = new BeanComparator(this); 322 bc.mOrderByName = mOrderByName; 323 bc.mUsingComparator = mUsingComparator; 324 bc.mFlags = mFlags ^ ((~mFlags & 0x01) << 1); 325 return bc; 326 } 327 328 337 public BeanComparator caseSensitive() { 338 if ((mFlags & 0x04) != 0) { 339 return this; 341 } 342 BeanComparator bc = new BeanComparator(this); 343 bc.mOrderByName = mOrderByName; 344 bc.mUsingComparator = mUsingComparator; 345 bc.mFlags = mFlags | 0x04; 346 return bc; 347 } 348 349 363 public BeanComparator collate(Comparator c) { 364 BeanComparator bc = new BeanComparator(this); 365 bc.mOrderByName = mOrderByName; 366 bc.mUsingComparator = mUsingComparator; 367 bc.mFlags = mFlags & ~0x04; 368 bc.mCollator = c; 369 return bc; 370 } 371 372 public int compare(Object obj1, Object obj2) throws ClassCastException { 373 Comparator c = mComparator; 374 if (c == null) { 375 c = mComparator = generateComparator(); 376 } 377 return c.compare(obj1, obj2); 378 } 379 380 public int hashCode() { 381 if (!mHasHashCode) { 382 setHashCode(new Rules(this)); 383 } 384 return mHashCode; 385 } 386 387 private void setHashCode(Rules rules) { 388 mHashCode = rules.hashCode(); 389 mHasHashCode = true; 390 } 391 392 400 public boolean equals(Object obj) { 401 if (obj instanceof BeanComparator) { 402 BeanComparator bc = (BeanComparator)obj; 403 404 return mFlags == bc.mFlags && 405 equalTest(mBeanClass, bc.mBeanClass) && 406 equalTest(mOrderByName, bc.mOrderByName) && 407 equalTest(mUsingComparator, bc.mUsingComparator) && 408 equalTest(mCollator, bc.mCollator) && 409 equalTest(mParent, bc.mParent); 410 } else { 411 return false; 412 } 413 } 414 415 private Map getProperties() { 416 if (mProperties == null) { 417 mProperties = BeanIntrospector.getAllProperties(mBeanClass); 418 } 419 return mProperties; 420 } 421 422 private Comparator generateComparator() { 423 Rules rules = new Rules(this); 424 425 if (!mHasHashCode) { 426 setHashCode(rules); 427 } 428 429 Class clazz; 430 431 synchronized (cGeneratedComparatorCache) { 432 Object c = cGeneratedComparatorCache.get(rules); 433 434 if (c == null) { 435 clazz = generateComparatorClass(rules); 436 cGeneratedComparatorCache.put(rules, clazz); 437 } else if (c instanceof Comparator ) { 438 return (Comparator )c; 439 } else { 440 clazz = (Class )c; 441 } 442 443 BeanComparator[] ruleParts = rules.getRuleParts(); 444 Comparator [] collators = new Comparator [ruleParts.length]; 445 Comparator [] usingComparators = new Comparator [ruleParts.length]; 446 boolean singleton = true; 447 448 for (int i=0; i<ruleParts.length; i++) { 449 BeanComparator rp = ruleParts[i]; 450 Comparator c2 = rp.mCollator; 451 if ((collators[i] = c2) != null) { 452 if (c2 != String.CASE_INSENSITIVE_ORDER) { 453 singleton = false; 454 } 455 } 456 if ((usingComparators[i] = rp.mUsingComparator) != null) { 457 singleton = false; 458 } 459 } 460 461 try { 462 Constructor ctor = clazz.getDeclaredConstructor 463 (new Class [] {Comparator [].class, Comparator [].class}); 464 c = (Comparator )ctor.newInstance 465 (new Object [] {collators, usingComparators}); 466 } catch (NoSuchMethodException e) { 467 throw new InternalError (e.toString()); 468 } catch (InstantiationException e) { 469 throw new InternalError (e.toString()); 470 } catch (IllegalAccessException e) { 471 throw new InternalError (e.toString()); 472 } catch (IllegalArgumentException e) { 473 throw new InternalError (e.toString()); 474 } catch (InvocationTargetException e) { 475 throw new InternalError (e.getTargetException().toString()); 476 } 477 478 if (singleton) { 479 cGeneratedComparatorCache.put(rules, c); 482 } 483 484 return (Comparator )c; 485 } 486 } 487 488 private Class generateComparatorClass(Rules rules) { 489 ClassInjector ci = ClassInjector.create 490 (getClass().getName(), mBeanClass.getClassLoader()); 491 return ci.defineClass(generateClassFile(ci.getClassName(), rules)); 492 } 493 494 private static ClassFile generateClassFile(String className, Rules rules) { 495 ClassFile cf = new ClassFile(className); 496 cf.markSynthetic(); 497 cf.setSourceFile(BeanComparator.class.getName()); 498 try { 499 cf.setTarget(System.getProperty("java.specification.version")); 500 } catch (Exception e) { 501 } 502 503 cf.addInterface(Comparator .class); 504 cf.addInterface(Serializable .class); 505 506 TypeDesc comparatorType = TypeDesc.forClass(Comparator .class); 508 TypeDesc comparatorArrayType = comparatorType.toArrayType(); 509 cf.addField(Modifiers.PRIVATE, 510 "mCollators", comparatorArrayType).markSynthetic(); 511 cf.addField(Modifiers.PRIVATE, 512 "mUsingComparators", comparatorArrayType).markSynthetic(); 513 514 TypeDesc[] paramTypes = { 516 comparatorArrayType, comparatorArrayType 517 }; 518 MethodInfo ctor = cf.addConstructor(Modifiers.PUBLIC, paramTypes); 519 ctor.markSynthetic(); 520 CodeBuilder builder = new CodeBuilder(ctor); 521 522 builder.loadThis(); 523 builder.invokeSuperConstructor(null); 524 builder.loadThis(); 525 builder.loadLocal(builder.getParameter(0)); 526 builder.storeField("mCollators", comparatorArrayType); 527 builder.loadThis(); 528 builder.loadLocal(builder.getParameter(1)); 529 builder.storeField("mUsingComparators", comparatorArrayType); 530 builder.returnVoid(); 531 532 Method compareMethod, compareToMethod; 534 try { 535 compareMethod = Comparator .class.getMethod 536 ("compare", new Class [] {Object .class, Object .class}); 537 compareToMethod = Comparable .class.getMethod 538 ("compareTo", new Class [] {Object .class}); 539 } catch (NoSuchMethodException e) { 540 throw new InternalError (e.toString()); 541 } 542 543 MethodInfo mi = cf.addMethod(compareMethod); 544 mi.markSynthetic(); 545 builder = new CodeBuilder(mi); 546 547 Label endLabel = builder.createLabel(); 548 LocalVariable obj1 = builder.getParameter(0); 549 LocalVariable obj2 = builder.getParameter(1); 550 551 554 BeanComparator[] ruleParts = rules.getRuleParts(); 555 BeanComparator bc = ruleParts[0]; 556 557 if ((bc.mFlags & 0x01) != 0) { 558 LocalVariable temp = obj1; 560 obj1 = obj2; 561 obj2 = temp; 562 } 563 564 builder.loadLocal(obj1); 566 builder.loadLocal(obj2); 567 builder.ifEqualBranch(endLabel, true); 568 569 boolean nullHigh = (bc.mFlags & 0x02) == 0; 571 Label label = builder.createLabel(); 572 builder.loadLocal(obj1); 573 builder.ifNullBranch(label, false); 574 builder.loadConstant(nullHigh ? 1 : -1); 575 builder.returnValue(TypeDesc.INT); 576 label.setLocation(); 577 label = builder.createLabel(); 578 builder.loadLocal(obj2); 579 builder.ifNullBranch(label, false); 580 builder.loadConstant(nullHigh ? -1 : 1); 581 builder.returnValue(TypeDesc.INT); 582 label.setLocation(); 583 584 LocalVariable result = 586 builder.createLocalVariable("result", TypeDesc.INT); 587 if (bc.mUsingComparator != null) { 588 builder.loadThis(); 589 builder.loadField("mUsingComparators", comparatorArrayType); 590 builder.loadConstant(0); 591 builder.loadFromArray(TypeDesc.forClass(Comparator .class)); 592 builder.loadLocal(obj1); 593 builder.loadLocal(obj2); 594 builder.invoke(compareMethod); 595 builder.storeLocal(result); 596 builder.loadLocal(result); 597 label = builder.createLabel(); 598 builder.ifZeroComparisonBranch(label, "=="); 599 builder.loadLocal(result); 600 builder.returnValue(TypeDesc.INT); 601 label.setLocation(); 602 } 603 604 TypeDesc type = TypeDesc.forClass(bc.mBeanClass); 607 builder.loadLocal(obj1); 608 builder.checkCast(type); 609 builder.storeLocal(obj1); 610 builder.loadLocal(obj2); 611 builder.checkCast(type); 612 builder.storeLocal(obj2); 613 614 for (int i=1; i<ruleParts.length; i++) { 616 bc = ruleParts[i]; 617 618 BeanProperty prop = 619 (BeanProperty)bc.getProperties().get(bc.mOrderByName); 620 Class propertyClass = prop.getType(); 621 TypeDesc propertyType = TypeDesc.forClass(propertyClass); 622 623 LocalVariable p1 = builder.createLocalVariable("p1", propertyType); 625 LocalVariable p2 = builder.createLocalVariable("p2", propertyType); 626 627 builder.loadLocal(obj1); 629 builder.invoke(prop.getReadMethod()); 630 builder.storeLocal(p1); 631 builder.loadLocal(obj2); 632 builder.invoke(prop.getReadMethod()); 633 builder.storeLocal(p2); 634 635 if ((bc.mFlags & 0x01) != 0) { 636 LocalVariable temp = p1; 638 p1 = p2; 639 p2 = temp; 640 } 641 642 Label nextLabel = builder.createLabel(); 643 644 if (!propertyClass.isPrimitive()) { 646 builder.loadLocal(p1); 647 builder.loadLocal(p2); 648 builder.ifEqualBranch(nextLabel, true); 649 650 nullHigh = (bc.mFlags & 0x02) == 0; 652 label = builder.createLabel(); 653 builder.loadLocal(p1); 654 builder.ifNullBranch(label, false); 655 builder.loadConstant(nullHigh ? 1 : -1); 656 builder.returnValue(TypeDesc.INT); 657 label.setLocation(); 658 label = builder.createLabel(); 659 builder.loadLocal(p2); 660 builder.ifNullBranch(label, false); 661 builder.loadConstant(nullHigh ? -1 : 1); 662 builder.returnValue(TypeDesc.INT); 663 label.setLocation(); 664 } 665 666 if (bc.mUsingComparator != null) { 669 builder.loadThis(); 670 builder.loadField("mUsingComparators", comparatorArrayType); 671 builder.loadConstant(i); 672 builder.loadFromArray(TypeDesc.forClass(Comparator .class)); 673 builder.loadLocal(p1); 674 builder.convert(propertyType, propertyType.toObjectType()); 675 builder.loadLocal(p2); 676 builder.convert(propertyType, propertyType.toObjectType()); 677 builder.invoke(compareMethod); 678 } else { 679 if ((bc.mFlags & 0x04) == 0 && bc.mCollator != null && 682 propertyClass.isAssignableFrom(String .class)) { 683 684 Label resultLabel = builder.createLabel(); 685 686 if (!String .class.isAssignableFrom(propertyClass)) { 687 691 TypeDesc stringType = TypeDesc.STRING; 692 693 builder.loadLocal(p1); 694 builder.instanceOf(stringType); 695 Label notString = builder.createLabel(); 696 builder.ifZeroComparisonBranch(notString, "=="); 697 builder.loadLocal(p2); 698 builder.instanceOf(stringType); 699 Label isString = builder.createLabel(); 700 builder.ifZeroComparisonBranch(isString, "!="); 701 702 notString.setLocation(); 703 generateComparableCompareTo 704 (builder, propertyClass, compareToMethod, 705 resultLabel, nextLabel, p1, p2); 706 707 isString.setLocation(); 708 } 709 710 builder.loadThis(); 711 builder.loadField("mCollators", comparatorArrayType); 712 builder.loadConstant(i); 713 builder.loadFromArray(TypeDesc.forClass(Comparator .class)); 714 builder.loadLocal(p1); 715 builder.loadLocal(p2); 716 builder.invoke(compareMethod); 717 718 resultLabel.setLocation(); 719 } else if (propertyClass.isPrimitive()) { 720 generatePrimitiveComparison(builder, propertyClass, p1,p2); 721 } else { 722 generateComparableCompareTo 724 (builder, propertyClass, compareToMethod, 725 null, nextLabel, p1, p2); 726 } 727 } 728 729 if (i < (ruleParts.length - 1)) { 730 builder.storeLocal(result); 731 builder.loadLocal(result); 732 builder.ifZeroComparisonBranch(nextLabel, "=="); 733 builder.loadLocal(result); 734 } 735 builder.returnValue(TypeDesc.INT); 736 737 nextLabel.setLocation(); 739 } 740 741 endLabel.setLocation(); 742 builder.loadConstant(0); 743 builder.returnValue(TypeDesc.INT); 744 745 return cf; 746 } 747 748 private static void generatePrimitiveComparison(CodeBuilder builder, 749 Class type, 750 LocalVariable a, 751 LocalVariable b) 752 { 753 if (type == float.class) { 754 Label done = builder.createLabel(); 756 757 builder.loadLocal(a); 758 builder.loadLocal(b); 759 builder.math(Opcode.FCMPG); 760 Label label = builder.createLabel(); 761 builder.ifZeroComparisonBranch(label, ">="); 762 builder.loadConstant(-1); 763 builder.branch(done); 764 765 label.setLocation(); 766 builder.loadLocal(a); 767 builder.loadLocal(b); 768 builder.math(Opcode.FCMPL); 769 label = builder.createLabel(); 770 builder.ifZeroComparisonBranch(label, "<="); 771 builder.loadConstant(1); 772 builder.branch(done); 773 774 Method floatToIntBits; 775 try { 776 floatToIntBits = Float .class.getMethod 777 ("floatToIntBits", new Class [] {float.class}); 778 } catch (NoSuchMethodException e) { 779 throw new InternalError (e.toString()); 780 } 781 782 label.setLocation(); 783 builder.loadLocal(a); 784 builder.invoke(floatToIntBits); 785 builder.convert(TypeDesc.INT, TypeDesc.LONG); 786 builder.loadLocal(b); 787 builder.invoke(floatToIntBits); 788 builder.convert(TypeDesc.INT, TypeDesc.LONG); 789 builder.math(Opcode.LCMP); 790 791 done.setLocation(); 792 } else if (type == double.class) { 793 Label done = builder.createLabel(); 795 796 builder.loadLocal(a); 797 builder.loadLocal(b); 798 done = builder.createLabel(); 799 builder.math(Opcode.DCMPG); 800 Label label = builder.createLabel(); 801 builder.ifZeroComparisonBranch(label, ">="); 802 builder.loadConstant(-1); 803 builder.branch(done); 804 805 label.setLocation(); 806 builder.loadLocal(a); 807 builder.loadLocal(b); 808 builder.math(Opcode.DCMPL); 809 label = builder.createLabel(); 810 builder.ifZeroComparisonBranch(label, "<="); 811 builder.loadConstant(1); 812 builder.branch(done); 813 814 Method doubleToLongBits; 815 try { 816 doubleToLongBits = Double .class.getMethod 817 ("doubleToLongBits", new Class [] {double.class}); 818 } catch (NoSuchMethodException e) { 819 throw new InternalError (e.toString()); 820 } 821 822 label.setLocation(); 823 builder.loadLocal(a); 824 builder.invoke(doubleToLongBits); 825 builder.loadLocal(b); 826 builder.invoke(doubleToLongBits); 827 builder.math(Opcode.LCMP); 828 829 done.setLocation(); 830 } else if (type == long.class) { 831 builder.loadLocal(a); 832 builder.loadLocal(b); 833 builder.math(Opcode.LCMP); 834 } else if (type == int.class) { 835 builder.loadLocal(a); 836 builder.convert(TypeDesc.INT, TypeDesc.LONG); 837 builder.loadLocal(b); 838 builder.convert(TypeDesc.INT, TypeDesc.LONG); 839 builder.math(Opcode.LCMP); 840 } else { 841 builder.loadLocal(a); 842 builder.loadLocal(b); 843 builder.math(Opcode.ISUB); 844 } 845 } 846 847 private static void generateComparableCompareTo(CodeBuilder builder, 848 Class type, 849 Method compareToMethod, 850 Label goodLabel, 851 Label nextLabel, 852 LocalVariable a, 853 LocalVariable b) 854 { 855 if (Comparable .class.isAssignableFrom(type)) { 856 builder.loadLocal(a); 857 builder.loadLocal(b); 858 builder.invoke(compareToMethod); 859 if (goodLabel != null) { 860 builder.branch(goodLabel); 861 } 862 } else { 863 TypeDesc comparableType = TypeDesc.forClass(Comparable .class); 865 866 boolean locateGoodLabel = false; 867 if (goodLabel == null) { 868 goodLabel = builder.createLabel(); 869 locateGoodLabel = true; 870 } 871 872 Label tryStart = builder.createLabel().setLocation(); 873 builder.loadLocal(a); 874 builder.checkCast(comparableType); 875 builder.loadLocal(b); 876 builder.checkCast(comparableType); 877 Label tryEnd = builder.createLabel().setLocation(); 878 builder.invoke(compareToMethod); 879 builder.branch(goodLabel); 880 881 builder.exceptionHandler(tryStart, tryEnd, 882 ClassCastException .class.getName()); 883 builder.pop(); 886 if (nextLabel == null) { 887 builder.loadConstant(0); 888 } else { 889 builder.branch(nextLabel); 890 } 891 892 if (locateGoodLabel) { 893 goodLabel.setLocation(); 894 } 895 } 896 } 897 898 private static class Rules { 900 private BeanComparator[] mRuleParts; 901 private int mHashCode; 902 903 public Rules(BeanComparator bc) { 904 mRuleParts = reduceRules(bc); 905 906 int hash = 0; 908 909 for (int i = mRuleParts.length - 1; i >= 0; i--) { 910 bc = mRuleParts[i]; 911 hash = 31 * hash; 912 913 hash += bc.mFlags << 4; 914 915 Object obj = bc.mBeanClass; 916 if (obj != null) { 917 hash += obj.hashCode(); 918 } 919 obj = bc.mOrderByName; 920 if (obj != null) { 921 hash += obj.hashCode(); 922 } 923 obj = bc.mUsingComparator; 924 if (obj != null) { 925 hash += obj.getClass().hashCode(); 926 } 927 obj = bc.mCollator; 928 if (obj != null) { 929 hash += obj.getClass().hashCode(); 930 } 931 } 932 933 mHashCode = hash; 934 } 935 936 public BeanComparator[] getRuleParts() { 937 return mRuleParts; 938 } 939 940 public int hashCode() { 941 return mHashCode; 942 } 943 944 948 public boolean equals(Object obj) { 949 if (!(obj instanceof Rules)) { 950 return false; 951 } 952 953 BeanComparator[] ruleParts1 = getRuleParts(); 954 BeanComparator[] ruleParts2 = ((Rules)obj).getRuleParts(); 955 956 if (ruleParts1.length != ruleParts2.length) { 957 return false; 958 } 959 960 for (int i=0; i<ruleParts1.length; i++) { 961 BeanComparator bc1 = ruleParts1[i]; 962 BeanComparator bc2 = ruleParts2[i]; 963 964 if (bc1.mFlags != bc2.mFlags) { 965 return false; 966 } 967 if (!equalTest(bc1.mBeanClass, bc2.mBeanClass)) { 968 return false; 969 } 970 if (!equalTest(bc1.mOrderByName, bc2.mOrderByName)) { 971 return false; 972 } 973 if ((bc1.mUsingComparator == null) != 974 (bc2.mUsingComparator == null)) { 975 return false; 976 } 977 if ((bc1.mCollator == null) != (bc2.mCollator == null)) { 978 return false; 979 } 980 } 981 982 return true; 983 } 984 985 private BeanComparator[] reduceRules(BeanComparator bc) { 986 List rules = new ArrayList (); 989 990 rules.add(bc); 991 String name = bc.mOrderByName; 992 993 while ((bc = bc.mParent) != null) { 994 if (name != bc.mOrderByName) { 996 rules.add(bc); 997 name = bc.mOrderByName; 998 } 999 } 1000 1001 int size = rules.size(); 1002 BeanComparator[] bcs = new BeanComparator[size]; 1003 for (int i=0; i<size; i++) { 1005 bcs[size - i - 1] = (BeanComparator)rules.get(i); 1006 } 1007 1008 return bcs; 1009 } 1010 } 1011} 1012 | Popular Tags |