Source Code Cross Referenced for BeanComparator.java in  » Byte-Code » Cojen » org » cojen » util » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Byte Code » Cojen » org.cojen.util 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         *  Copyright 2004 Brian S O'Neill
0003:         *
0004:         *  Licensed under the Apache License, Version 2.0 (the "License");
0005:         *  you may not use this file except in compliance with the License.
0006:         *  You may obtain a copy of the License at
0007:         *
0008:         *      http://www.apache.org/licenses/LICENSE-2.0
0009:         *
0010:         *  Unless required by applicable law or agreed to in writing, software
0011:         *  distributed under the License is distributed on an "AS IS" BASIS,
0012:         *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013:         *  See the License for the specific language governing permissions and
0014:         *  limitations under the License.
0015:         */
0016:
0017:        package org.cojen.util;
0018:
0019:        import java.io.Serializable;
0020:        import java.util.ArrayList;
0021:        import java.util.Comparator;
0022:        import java.util.List;
0023:        import java.util.Map;
0024:        import java.lang.reflect.Constructor;
0025:        import java.lang.reflect.Method;
0026:        import java.lang.reflect.InvocationTargetException;
0027:        import org.cojen.classfile.ClassFile;
0028:        import org.cojen.classfile.CodeBuilder;
0029:        import org.cojen.classfile.Label;
0030:        import org.cojen.classfile.LocalVariable;
0031:        import org.cojen.classfile.MethodInfo;
0032:        import org.cojen.classfile.Modifiers;
0033:        import org.cojen.classfile.Opcode;
0034:        import org.cojen.classfile.TypeDesc;
0035:
0036:        /**
0037:         * A highly customizable, high-performance Comparator, designed specifically
0038:         * for advanced sorting of JavaBeans. BeanComparators contain dynamically
0039:         * auto-generated code and perform as well as hand written Comparators.
0040:         * <p>
0041:         * BeanComparator instances are immutable; order customization methods
0042:         * return new BeanComparators with refined rules. Calls to customizers can
0043:         * be chained together to read like a formula. The following example produces
0044:         * a Comparator that orders Threads by name, thread group name, and reverse
0045:         * priority.
0046:         *
0047:         * <pre>
0048:         * Comparator c = BeanComparator.forClass(Thread.class)
0049:         *     .orderBy("name")
0050:         *     .orderBy("threadGroup.name")
0051:         *     .orderBy("-priority");
0052:         * </pre>
0053:         *
0054:         * The results of sorting Threads using this Comparator and displaying the
0055:         * results in a table may look like this:
0056:         *
0057:         * <p><table border="2">
0058:         * <tr><th>name</th><th>threadGroup.name</th><th>priority</th></tr>
0059:         * <tr><td>daemon</td><td>appGroup</td><td>9</td></tr>
0060:         * <tr><td>main</td><td>main</td><td>5</td></tr>
0061:         * <tr><td>main</td><td>secureGroup</td><td>5</td></tr>
0062:         * <tr><td>sweeper</td><td>main</td><td>1</td></tr>
0063:         * <tr><td>Thread-0</td><td>main</td><td>5</td></tr>
0064:         * <tr><td>Thread-1</td><td>main</td><td>5</td></tr>
0065:         * <tr><td>worker</td><td>appGroup</td><td>8</td></tr>
0066:         * <tr><td>worker</td><td>appGroup</td><td>5</td></tr>
0067:         * <tr><td>worker</td><td>secureGroup</td><td>8</td></tr>
0068:         * <tr><td>worker</td><td>secureGroup</td><td>5</td></tr>
0069:         * </table><p>
0070:         *
0071:         * An equivalent Thread ordering Comparator may be specified as:
0072:         *
0073:         * <pre>
0074:         * Comparator c = BeanComparator.forClass(Thread.class)
0075:         *     .orderBy("name")
0076:         *     .orderBy("threadGroup")
0077:         *     .using(BeanComparator.forClass(ThreadGroup.class).orderBy("name"))
0078:         *     .orderBy("priority")
0079:         *     .reverse();
0080:         * </pre>
0081:         *
0082:         * The current implementation of BeanComparator has been optimized for fast
0083:         * construction and execution of BeanComparators. For maximum performance,
0084:         * however, save and re-use BeanComparators wherever possible.
0085:         * <p>
0086:         * Even though BeanComparator makes use of auto-generated code, instances are
0087:         * fully Serializable, as long as all passed in Comparators are also
0088:         * Serializable.
0089:         *
0090:         * @author Brian S O'Neill
0091:         */
0092:        public class BeanComparator implements  Comparator, Serializable {
0093:            // Maps Rules to auto-generated Comparators.
0094:            private static Map cGeneratedComparatorCache;
0095:
0096:            static {
0097:                cGeneratedComparatorCache = new SoftValuedHashMap();
0098:            }
0099:
0100:            /**
0101:             * Get or create a new BeanComparator for beans of the given type. Without
0102:             * any {@link #orderBy order-by} properties specified, the returned
0103:             * BeanComparator can only order against null beans (null is 
0104:             * {@link #nullHigh high} by default), and treats all other comparisons as
0105:             * equal.
0106:             */
0107:            public static BeanComparator forClass(Class clazz) {
0108:                return new BeanComparator(clazz);
0109:            }
0110:
0111:            /**
0112:             * Compare two objects for equality.
0113:             */
0114:            private static boolean equalTest(Object obj1, Object obj2) {
0115:                return (obj1 == obj2) ? true
0116:                        : ((obj1 == null || obj2 == null) ? false : obj1
0117:                                .equals(obj2));
0118:            }
0119:
0120:            /**
0121:             * Compare two object classes for equality.
0122:             */
0123:            /*
0124:            private static boolean equalClassTest(Object obj1, Object obj2) {
0125:                return (obj1 == obj2) ? true :
0126:                    ((obj1 == null || obj2 == null) ? false :
0127:                     obj1.getClass().equals(obj2.getClass()));
0128:            }
0129:             */
0130:
0131:            private Class mBeanClass;
0132:
0133:            // Maps property names to PropertyDescriptors.
0134:            private transient Map mProperties;
0135:
0136:            private String mOrderByName;
0137:
0138:            private Comparator mUsingComparator;
0139:
0140:            // bit 0: reverse
0141:            // bit 1: null low order
0142:            // bit 2: use String compareTo instead of collator
0143:            private int mFlags;
0144:
0145:            // Used for comparing strings.
0146:            private Comparator mCollator;
0147:
0148:            private BeanComparator mParent;
0149:
0150:            // Auto-generated internal Comparator.
0151:            private transient Comparator mComparator;
0152:
0153:            private transient boolean mHasHashCode;
0154:            private transient int mHashCode;
0155:
0156:            private BeanComparator(Class clazz) {
0157:                mBeanClass = clazz;
0158:                mCollator = String.CASE_INSENSITIVE_ORDER;
0159:            }
0160:
0161:            private BeanComparator(BeanComparator parent) {
0162:                mParent = parent;
0163:                mBeanClass = parent.mBeanClass;
0164:                mProperties = parent.getProperties();
0165:                mCollator = parent.mCollator;
0166:            }
0167:
0168:            /**
0169:             * Add an order-by property to produce a more refined Comparator. If the
0170:             * property does not return a {@link Comparable} object when
0171:             * {@link #compare compare} is called on the returned comparator, the
0172:             * property is ignored. Call {@link #using using} on the returned
0173:             * BeanComparator to specify a Comparator to use for this property instead.
0174:             * <p>
0175:             * The specified propery name may refer to sub-properties using a dot
0176:             * notation. For example, if the bean being compared contains a property
0177:             * named "info" of type "Information", and "Information" contains a
0178:             * property named "text", then ordering by the info text can be specified
0179:             * by "info.text". Sub-properties of sub-properties may be refered to as
0180:             * well, a.b.c.d.e etc.
0181:             * <p>
0182:             * If property type is a primitive, ordering is the same as for its
0183:             * Comparable object peer. Primitive booleans are ordered false low, true
0184:             * high. Floating point primitves are ordered exactly the same way as
0185:             * {@link Float#compareTo(Float) Float.compareTo} and
0186:             * {@link Double#compareTo(Double) Double.compareTo}.
0187:             * <p>
0188:             * As a convenience, property names may have a '-' or '+' character prefix
0189:             * to specify sort order. A prefix of '-' indicates that the property
0190:             * is to be sorted in reverse (descending). By default, properties are
0191:             * sorted in ascending order, and so a prefix of '+' has no effect.
0192:             * <p>
0193:             * Any previously applied {@link #reverse reverse-order}, {@link #nullHigh
0194:             * null-order} and {@link #caseSensitive case-sensitive} settings are not
0195:             * carried over, and are reset to the defaults for this order-by property.
0196:             *
0197:             * @throws IllegalArgumentException when property doesn't exist or cannot
0198:             * be read.
0199:             */
0200:            public BeanComparator orderBy(String propertyName)
0201:                    throws IllegalArgumentException {
0202:                int dot = propertyName.indexOf('.');
0203:                String subName;
0204:                if (dot < 0) {
0205:                    subName = null;
0206:                } else {
0207:                    subName = propertyName.substring(dot + 1);
0208:                    propertyName = propertyName.substring(0, dot);
0209:                }
0210:
0211:                boolean reverse = false;
0212:                if (propertyName.length() > 0) {
0213:                    char prefix = propertyName.charAt(0);
0214:                    switch (prefix) {
0215:                    default:
0216:                        break;
0217:                    case '-':
0218:                        reverse = true;
0219:                        // Fall through
0220:                    case '+':
0221:                        propertyName = propertyName.substring(1);
0222:                    }
0223:                }
0224:
0225:                BeanProperty prop = (BeanProperty) getProperties().get(
0226:                        propertyName);
0227:
0228:                if (prop == null) {
0229:                    throw new IllegalArgumentException("Property '"
0230:                            + propertyName + "' doesn't exist in '"
0231:                            + mBeanClass.getName() + '\'');
0232:                }
0233:
0234:                if (prop.getReadMethod() == null) {
0235:                    throw new IllegalArgumentException("Property '"
0236:                            + propertyName + "' cannot be read");
0237:                }
0238:
0239:                if (propertyName.equals(mOrderByName)) {
0240:                    // Make String unique so that properties can be specified in
0241:                    // consecutive order-by calls without being eliminated by
0242:                    // reduceRules. A secondary order-by may wish to further refine an
0243:                    // ambiguous comparison using a Comparator.
0244:                    propertyName = new String(propertyName);
0245:                }
0246:
0247:                BeanComparator bc = new BeanComparator(this );
0248:                bc.mOrderByName = propertyName;
0249:
0250:                if (subName != null) {
0251:                    BeanComparator subOrder = forClass(prop.getType());
0252:                    subOrder.mCollator = mCollator;
0253:                    bc = bc.using(subOrder.orderBy(subName));
0254:                }
0255:
0256:                return reverse ? bc.reverse() : bc;
0257:            }
0258:
0259:            /**
0260:             * Specifiy a Comparator to use on just the last {@link #orderBy order-by}
0261:             * property. This is good for comparing properties that are not
0262:             * {@link Comparable} or for applying special ordering rules for a
0263:             * property. If no order-by properties have been specified, then Comparator
0264:             * is applied to the compared beans.
0265:             * <p>
0266:             * Any previously applied String {@link #caseSensitive case-sensitive} or
0267:             * {@link #collate collator} settings are overridden by this Comparator.
0268:             * If property values being compared are primitive, they are converted to
0269:             * their object peers before being passed to the Comparator.
0270:             *
0271:             * @param c Comparator to use on the last order-by property. Passing null
0272:             * restores the default comparison for the last order-by property.
0273:             */
0274:            public BeanComparator using(Comparator c) {
0275:                BeanComparator bc = new BeanComparator(this );
0276:                bc.mOrderByName = mOrderByName;
0277:                bc.mUsingComparator = c;
0278:                bc.mFlags = mFlags;
0279:                return bc;
0280:            }
0281:
0282:            /**
0283:             * Toggle reverse-order option on just the last {@link #orderBy order-by}
0284:             * property. By default, order is ascending. If no order-by properties have
0285:             * been specified, then reverse order is applied to the compared beans.
0286:             */
0287:            public BeanComparator reverse() {
0288:                BeanComparator bc = new BeanComparator(this );
0289:                bc.mOrderByName = mOrderByName;
0290:                bc.mUsingComparator = mUsingComparator;
0291:                bc.mFlags = mFlags ^ 0x01;
0292:                return bc;
0293:            }
0294:
0295:            /**
0296:             * Set the order of comparisons against null as being high (the default)
0297:             * on just the last {@link #orderBy order-by} property. If no order-by
0298:             * properties have been specified, then null high order is applied to the
0299:             * compared beans. Null high order is the default for consistency with the
0300:             * high ordering of {@link Float#NaN NaN} by
0301:             * {@link Float#compareTo(Float) Float}.
0302:             * <p>
0303:             * Calling 'nullHigh, reverse' is equivalent to calling 'reverse, nullLow'.
0304:             */
0305:            public BeanComparator nullHigh() {
0306:                BeanComparator bc = new BeanComparator(this );
0307:                bc.mOrderByName = mOrderByName;
0308:                bc.mUsingComparator = mUsingComparator;
0309:                bc.mFlags = mFlags ^ ((mFlags & 0x01) << 1);
0310:                return bc;
0311:            }
0312:
0313:            /**
0314:             * Set the order of comparisons against null as being low
0315:             * on just the last {@link #orderBy order-by} property. If no order-by
0316:             * properties have been specified, then null low order is applied to the
0317:             * compared beans.
0318:             * <p>
0319:             * Calling 'reverse, nullLow' is equivalent to calling 'nullHigh, reverse'.
0320:             */
0321:            public BeanComparator nullLow() {
0322:                BeanComparator bc = new BeanComparator(this );
0323:                bc.mOrderByName = mOrderByName;
0324:                bc.mUsingComparator = mUsingComparator;
0325:                bc.mFlags = mFlags ^ ((~mFlags & 0x01) << 1);
0326:                return bc;
0327:            }
0328:
0329:            /**
0330:             * Override the collator and compare just the last order-by property using
0331:             * {@link String#compareTo(String) String.compareTo}, if it is of type
0332:             * String. If no order-by properties have been specified then this call is
0333:             * ineffective.
0334:             * <p>
0335:             * A {@link #using using} Comparator disables this setting. Passing null to
0336:             * the using method will re-enable a case-sensitive setting.
0337:             */
0338:            public BeanComparator caseSensitive() {
0339:                if ((mFlags & 0x04) != 0) {
0340:                    // Already case-sensitive.
0341:                    return this ;
0342:                }
0343:                BeanComparator bc = new BeanComparator(this );
0344:                bc.mOrderByName = mOrderByName;
0345:                bc.mUsingComparator = mUsingComparator;
0346:                bc.mFlags = mFlags | 0x04;
0347:                return bc;
0348:            }
0349:
0350:            /**
0351:             * Set a Comparator for ordering Strings, which is passed on to all
0352:             * BeanComparators derived from this one. By default, String are compared
0353:             * using {@link String#CASE_INSENSITIVE_ORDER}. Passing null for a collator
0354:             * will cause all String comparisons to use
0355:             * {@link String#compareTo(String) String.compareTo}.
0356:             * <p>
0357:             * A {@link #using using} Comparator disables this setting. Passing null
0358:             * to the using method will re-enable a collator.
0359:             *
0360:             * @param c Comparator to use for ordering all Strings. Passing null
0361:             * causes all Strings to be ordered by
0362:             * {@link String#compareTo(String) String.compareTo}.
0363:             */
0364:            public BeanComparator collate(Comparator c) {
0365:                BeanComparator bc = new BeanComparator(this );
0366:                bc.mOrderByName = mOrderByName;
0367:                bc.mUsingComparator = mUsingComparator;
0368:                bc.mFlags = mFlags & ~0x04;
0369:                bc.mCollator = c;
0370:                return bc;
0371:            }
0372:
0373:            public int compare(Object obj1, Object obj2)
0374:                    throws ClassCastException {
0375:                Comparator c = mComparator;
0376:                if (c == null) {
0377:                    c = mComparator = generateComparator();
0378:                }
0379:                return c.compare(obj1, obj2);
0380:            }
0381:
0382:            public int hashCode() {
0383:                if (!mHasHashCode) {
0384:                    setHashCode(new Rules(this ));
0385:                }
0386:                return mHashCode;
0387:            }
0388:
0389:            private void setHashCode(Rules rules) {
0390:                mHashCode = rules.hashCode();
0391:                mHasHashCode = true;
0392:            }
0393:
0394:            /**
0395:             * Compares BeanComparators for equality based on their imposed ordering.
0396:             * Returns true only if the given object is a BeanComparater and it can be
0397:             * determined without a doubt that the ordering is identical. Because
0398:             * equality testing is dependent on the behavior of the equals methods of
0399:             * any 'using' Comparators and/or collators, false may be returned even
0400:             * though ordering is in fact identical.
0401:             */
0402:            public boolean equals(Object obj) {
0403:                if (obj instanceof  BeanComparator) {
0404:                    BeanComparator bc = (BeanComparator) obj;
0405:
0406:                    return mFlags == bc.mFlags
0407:                            && equalTest(mBeanClass, bc.mBeanClass)
0408:                            && equalTest(mOrderByName, bc.mOrderByName)
0409:                            && equalTest(mUsingComparator, bc.mUsingComparator)
0410:                            && equalTest(mCollator, bc.mCollator)
0411:                            && equalTest(mParent, bc.mParent);
0412:                } else {
0413:                    return false;
0414:                }
0415:            }
0416:
0417:            private Map getProperties() {
0418:                if (mProperties == null) {
0419:                    mProperties = BeanIntrospector.getAllProperties(mBeanClass);
0420:                }
0421:                return mProperties;
0422:            }
0423:
0424:            private Comparator generateComparator() {
0425:                Rules rules = new Rules(this );
0426:
0427:                if (!mHasHashCode) {
0428:                    setHashCode(rules);
0429:                }
0430:
0431:                Class clazz;
0432:
0433:                synchronized (cGeneratedComparatorCache) {
0434:                    Object c = cGeneratedComparatorCache.get(rules);
0435:
0436:                    if (c == null) {
0437:                        clazz = generateComparatorClass(rules);
0438:                        cGeneratedComparatorCache.put(rules, clazz);
0439:                    } else if (c instanceof  Comparator) {
0440:                        return (Comparator) c;
0441:                    } else {
0442:                        clazz = (Class) c;
0443:                    }
0444:
0445:                    BeanComparator[] ruleParts = rules.getRuleParts();
0446:                    Comparator[] collators = new Comparator[ruleParts.length];
0447:                    Comparator[] usingComparators = new Comparator[ruleParts.length];
0448:                    boolean singleton = true;
0449:
0450:                    for (int i = 0; i < ruleParts.length; i++) {
0451:                        BeanComparator rp = ruleParts[i];
0452:                        Comparator c2 = rp.mCollator;
0453:                        if ((collators[i] = c2) != null) {
0454:                            if (c2 != String.CASE_INSENSITIVE_ORDER) {
0455:                                singleton = false;
0456:                            }
0457:                        }
0458:                        if ((usingComparators[i] = rp.mUsingComparator) != null) {
0459:                            singleton = false;
0460:                        }
0461:                    }
0462:
0463:                    try {
0464:                        Constructor ctor = clazz
0465:                                .getDeclaredConstructor(new Class[] {
0466:                                        Comparator[].class, Comparator[].class });
0467:                        c = (Comparator) ctor.newInstance(new Object[] {
0468:                                collators, usingComparators });
0469:                    } catch (NoSuchMethodException e) {
0470:                        throw new InternalError(e.toString());
0471:                    } catch (InstantiationException e) {
0472:                        throw new InternalError(e.toString());
0473:                    } catch (IllegalAccessException e) {
0474:                        throw new InternalError(e.toString());
0475:                    } catch (IllegalArgumentException e) {
0476:                        throw new InternalError(e.toString());
0477:                    } catch (InvocationTargetException e) {
0478:                        throw new InternalError(e.getTargetException()
0479:                                .toString());
0480:                    }
0481:
0482:                    if (singleton) {
0483:                        // Can save and re-use instance since it obeys the requirements
0484:                        // for a singleton.
0485:                        cGeneratedComparatorCache.put(rules, c);
0486:                    }
0487:
0488:                    return (Comparator) c;
0489:                }
0490:            }
0491:
0492:            private Class generateComparatorClass(Rules rules) {
0493:                ClassInjector ci = ClassInjector.create(getClass().getName(),
0494:                        mBeanClass.getClassLoader());
0495:                return ci.defineClass(generateClassFile(ci.getClassName(),
0496:                        rules));
0497:            }
0498:
0499:            private static ClassFile generateClassFile(String className,
0500:                    Rules rules) {
0501:                ClassFile cf = new ClassFile(className);
0502:                cf.markSynthetic();
0503:                cf.setSourceFile(BeanComparator.class.getName());
0504:                try {
0505:                    cf.setTarget(System
0506:                            .getProperty("java.specification.version"));
0507:                } catch (Exception e) {
0508:                }
0509:
0510:                cf.addInterface(Comparator.class);
0511:                cf.addInterface(Serializable.class);
0512:
0513:                // Define fields to hold usage comparator and collator.
0514:                TypeDesc comparatorType = TypeDesc.forClass(Comparator.class);
0515:                TypeDesc comparatorArrayType = comparatorType.toArrayType();
0516:                cf.addField(Modifiers.PRIVATE, "mCollators",
0517:                        comparatorArrayType).markSynthetic();
0518:                cf.addField(Modifiers.PRIVATE, "mUsingComparators",
0519:                        comparatorArrayType).markSynthetic();
0520:
0521:                // Create constructor to initialize fields.
0522:                TypeDesc[] paramTypes = { comparatorArrayType,
0523:                        comparatorArrayType };
0524:                MethodInfo ctor = cf.addConstructor(Modifiers.PUBLIC,
0525:                        paramTypes);
0526:                ctor.markSynthetic();
0527:                CodeBuilder builder = new CodeBuilder(ctor);
0528:
0529:                builder.loadThis();
0530:                builder.invokeSuperConstructor(null);
0531:                builder.loadThis();
0532:                builder.loadLocal(builder.getParameter(0));
0533:                builder.storeField("mCollators", comparatorArrayType);
0534:                builder.loadThis();
0535:                builder.loadLocal(builder.getParameter(1));
0536:                builder.storeField("mUsingComparators", comparatorArrayType);
0537:                builder.returnVoid();
0538:
0539:                // Create the all-important compare method.
0540:                Method compareMethod, compareToMethod;
0541:                try {
0542:                    compareMethod = Comparator.class.getMethod("compare",
0543:                            new Class[] { Object.class, Object.class });
0544:                    compareToMethod = Comparable.class.getMethod("compareTo",
0545:                            new Class[] { Object.class });
0546:                } catch (NoSuchMethodException e) {
0547:                    throw new InternalError(e.toString());
0548:                }
0549:
0550:                MethodInfo mi = cf.addMethod(compareMethod);
0551:                mi.markSynthetic();
0552:                builder = new CodeBuilder(mi);
0553:
0554:                Label endLabel = builder.createLabel();
0555:                LocalVariable obj1 = builder.getParameter(0);
0556:                LocalVariable obj2 = builder.getParameter(1);
0557:
0558:                // The first rule always applies to the beans directly. All others
0559:                // apply to properties.
0560:
0561:                BeanComparator[] ruleParts = rules.getRuleParts();
0562:                BeanComparator bc = ruleParts[0];
0563:
0564:                if ((bc.mFlags & 0x01) != 0) {
0565:                    // Reverse beans.
0566:                    LocalVariable temp = obj1;
0567:                    obj1 = obj2;
0568:                    obj2 = temp;
0569:                }
0570:
0571:                // Handle the case when obj1 and obj2 are the same (or both null)
0572:                builder.loadLocal(obj1);
0573:                builder.loadLocal(obj2);
0574:                builder.ifEqualBranch(endLabel, true);
0575:
0576:                // Do null order checks for beans.
0577:                boolean nullHigh = (bc.mFlags & 0x02) == 0;
0578:                Label label = builder.createLabel();
0579:                builder.loadLocal(obj1);
0580:                builder.ifNullBranch(label, false);
0581:                builder.loadConstant(nullHigh ? 1 : -1);
0582:                builder.returnValue(TypeDesc.INT);
0583:                label.setLocation();
0584:                label = builder.createLabel();
0585:                builder.loadLocal(obj2);
0586:                builder.ifNullBranch(label, false);
0587:                builder.loadConstant(nullHigh ? -1 : 1);
0588:                builder.returnValue(TypeDesc.INT);
0589:                label.setLocation();
0590:
0591:                // Call 'using' Comparator if one is provided.
0592:                LocalVariable result = builder.createLocalVariable("result",
0593:                        TypeDesc.INT);
0594:                if (bc.mUsingComparator != null) {
0595:                    builder.loadThis();
0596:                    builder.loadField("mUsingComparators", comparatorArrayType);
0597:                    builder.loadConstant(0);
0598:                    builder.loadFromArray(TypeDesc.forClass(Comparator.class));
0599:                    builder.loadLocal(obj1);
0600:                    builder.loadLocal(obj2);
0601:                    builder.invoke(compareMethod);
0602:                    builder.storeLocal(result);
0603:                    builder.loadLocal(result);
0604:                    label = builder.createLabel();
0605:                    builder.ifZeroComparisonBranch(label, "==");
0606:                    builder.loadLocal(result);
0607:                    builder.returnValue(TypeDesc.INT);
0608:                    label.setLocation();
0609:                }
0610:
0611:                // Cast bean parameters to correct types so that properties may be
0612:                // accessed.
0613:                TypeDesc type = TypeDesc.forClass(bc.mBeanClass);
0614:                builder.loadLocal(obj1);
0615:                builder.checkCast(type);
0616:                builder.storeLocal(obj1);
0617:                builder.loadLocal(obj2);
0618:                builder.checkCast(type);
0619:                builder.storeLocal(obj2);
0620:
0621:                // Generate code to perform comparisons against each property.
0622:                for (int i = 1; i < ruleParts.length; i++) {
0623:                    bc = ruleParts[i];
0624:
0625:                    BeanProperty prop = (BeanProperty) bc.getProperties().get(
0626:                            bc.mOrderByName);
0627:                    Class propertyClass = prop.getType();
0628:                    TypeDesc propertyType = TypeDesc.forClass(propertyClass);
0629:
0630:                    // Create local variable to hold property values.
0631:                    LocalVariable p1 = builder.createLocalVariable("p1",
0632:                            propertyType);
0633:                    LocalVariable p2 = builder.createLocalVariable("p2",
0634:                            propertyType);
0635:
0636:                    // Access properties and store in local variables.
0637:                    builder.loadLocal(obj1);
0638:                    builder.invoke(prop.getReadMethod());
0639:                    builder.storeLocal(p1);
0640:                    builder.loadLocal(obj2);
0641:                    builder.invoke(prop.getReadMethod());
0642:                    builder.storeLocal(p2);
0643:
0644:                    if ((bc.mFlags & 0x01) != 0) {
0645:                        // Reverse properties.
0646:                        LocalVariable temp = p1;
0647:                        p1 = p2;
0648:                        p2 = temp;
0649:                    }
0650:
0651:                    Label nextLabel = builder.createLabel();
0652:
0653:                    // Handle the case when p1 and p2 are the same (or both null)
0654:                    if (!propertyClass.isPrimitive()) {
0655:                        builder.loadLocal(p1);
0656:                        builder.loadLocal(p2);
0657:                        builder.ifEqualBranch(nextLabel, true);
0658:
0659:                        // Do null order checks for properties.
0660:                        nullHigh = (bc.mFlags & 0x02) == 0;
0661:                        label = builder.createLabel();
0662:                        builder.loadLocal(p1);
0663:                        builder.ifNullBranch(label, false);
0664:                        builder.loadConstant(nullHigh ? 1 : -1);
0665:                        builder.returnValue(TypeDesc.INT);
0666:                        label.setLocation();
0667:                        label = builder.createLabel();
0668:                        builder.loadLocal(p2);
0669:                        builder.ifNullBranch(label, false);
0670:                        builder.loadConstant(nullHigh ? -1 : 1);
0671:                        builder.returnValue(TypeDesc.INT);
0672:                        label.setLocation();
0673:                    }
0674:
0675:                    // Call 'using' Comparator if one is provided, else assume
0676:                    // Comparable.
0677:                    if (bc.mUsingComparator != null) {
0678:                        builder.loadThis();
0679:                        builder.loadField("mUsingComparators",
0680:                                comparatorArrayType);
0681:                        builder.loadConstant(i);
0682:                        builder.loadFromArray(TypeDesc
0683:                                .forClass(Comparator.class));
0684:                        builder.loadLocal(p1);
0685:                        builder.convert(propertyType, propertyType
0686:                                .toObjectType());
0687:                        builder.loadLocal(p2);
0688:                        builder.convert(propertyType, propertyType
0689:                                .toObjectType());
0690:                        builder.invoke(compareMethod);
0691:                    } else {
0692:                        // If case-sensitive is off and a collator is provided and
0693:                        // property could be a String, apply collator.
0694:                        if ((bc.mFlags & 0x04) == 0 && bc.mCollator != null
0695:                                && propertyClass.isAssignableFrom(String.class)) {
0696:
0697:                            Label resultLabel = builder.createLabel();
0698:
0699:                            if (!String.class.isAssignableFrom(propertyClass)) {
0700:                                // Check if both property values are strings at
0701:                                // runtime. If they aren't, cast to Comparable and call
0702:                                // compareTo.
0703:
0704:                                TypeDesc stringType = TypeDesc.STRING;
0705:
0706:                                builder.loadLocal(p1);
0707:                                builder.instanceOf(stringType);
0708:                                Label notString = builder.createLabel();
0709:                                builder.ifZeroComparisonBranch(notString, "==");
0710:                                builder.loadLocal(p2);
0711:                                builder.instanceOf(stringType);
0712:                                Label isString = builder.createLabel();
0713:                                builder.ifZeroComparisonBranch(isString, "!=");
0714:
0715:                                notString.setLocation();
0716:                                generateComparableCompareTo(builder,
0717:                                        propertyClass, compareToMethod,
0718:                                        resultLabel, nextLabel, p1, p2);
0719:
0720:                                isString.setLocation();
0721:                            }
0722:
0723:                            builder.loadThis();
0724:                            builder
0725:                                    .loadField("mCollators",
0726:                                            comparatorArrayType);
0727:                            builder.loadConstant(i);
0728:                            builder.loadFromArray(TypeDesc
0729:                                    .forClass(Comparator.class));
0730:                            builder.loadLocal(p1);
0731:                            builder.loadLocal(p2);
0732:                            builder.invoke(compareMethod);
0733:
0734:                            resultLabel.setLocation();
0735:                        } else if (propertyClass.isPrimitive()) {
0736:                            generatePrimitiveComparison(builder, propertyClass,
0737:                                    p1, p2);
0738:                        } else {
0739:                            // Assume properties are instances of Comparable.
0740:                            generateComparableCompareTo(builder, propertyClass,
0741:                                    compareToMethod, null, nextLabel, p1, p2);
0742:                        }
0743:                    }
0744:
0745:                    if (i < (ruleParts.length - 1)) {
0746:                        builder.storeLocal(result);
0747:                        builder.loadLocal(result);
0748:                        builder.ifZeroComparisonBranch(nextLabel, "==");
0749:                        builder.loadLocal(result);
0750:                    }
0751:                    builder.returnValue(TypeDesc.INT);
0752:
0753:                    // The next property comparison will start here.
0754:                    nextLabel.setLocation();
0755:                }
0756:
0757:                endLabel.setLocation();
0758:                builder.loadConstant(0);
0759:                builder.returnValue(TypeDesc.INT);
0760:
0761:                return cf;
0762:            }
0763:
0764:            private static void generatePrimitiveComparison(
0765:                    CodeBuilder builder, Class type, LocalVariable a,
0766:                    LocalVariable b) {
0767:                if (type == float.class) {
0768:                    // Comparison is same as for Float.compareTo(Float).
0769:                    Label done = builder.createLabel();
0770:
0771:                    builder.loadLocal(a);
0772:                    builder.loadLocal(b);
0773:                    builder.math(Opcode.FCMPG);
0774:                    Label label = builder.createLabel();
0775:                    builder.ifZeroComparisonBranch(label, ">=");
0776:                    builder.loadConstant(-1);
0777:                    builder.branch(done);
0778:
0779:                    label.setLocation();
0780:                    builder.loadLocal(a);
0781:                    builder.loadLocal(b);
0782:                    builder.math(Opcode.FCMPL);
0783:                    label = builder.createLabel();
0784:                    builder.ifZeroComparisonBranch(label, "<=");
0785:                    builder.loadConstant(1);
0786:                    builder.branch(done);
0787:
0788:                    Method floatToIntBits;
0789:                    try {
0790:                        floatToIntBits = Float.class.getMethod(
0791:                                "floatToIntBits", new Class[] { float.class });
0792:                    } catch (NoSuchMethodException e) {
0793:                        throw new InternalError(e.toString());
0794:                    }
0795:
0796:                    label.setLocation();
0797:                    builder.loadLocal(a);
0798:                    builder.invoke(floatToIntBits);
0799:                    builder.convert(TypeDesc.INT, TypeDesc.LONG);
0800:                    builder.loadLocal(b);
0801:                    builder.invoke(floatToIntBits);
0802:                    builder.convert(TypeDesc.INT, TypeDesc.LONG);
0803:                    builder.math(Opcode.LCMP);
0804:
0805:                    done.setLocation();
0806:                } else if (type == double.class) {
0807:                    // Comparison is same as for Double.compareTo(Double).
0808:                    Label done = builder.createLabel();
0809:
0810:                    builder.loadLocal(a);
0811:                    builder.loadLocal(b);
0812:                    done = builder.createLabel();
0813:                    builder.math(Opcode.DCMPG);
0814:                    Label label = builder.createLabel();
0815:                    builder.ifZeroComparisonBranch(label, ">=");
0816:                    builder.loadConstant(-1);
0817:                    builder.branch(done);
0818:
0819:                    label.setLocation();
0820:                    builder.loadLocal(a);
0821:                    builder.loadLocal(b);
0822:                    builder.math(Opcode.DCMPL);
0823:                    label = builder.createLabel();
0824:                    builder.ifZeroComparisonBranch(label, "<=");
0825:                    builder.loadConstant(1);
0826:                    builder.branch(done);
0827:
0828:                    Method doubleToLongBits;
0829:                    try {
0830:                        doubleToLongBits = Double.class.getMethod(
0831:                                "doubleToLongBits",
0832:                                new Class[] { double.class });
0833:                    } catch (NoSuchMethodException e) {
0834:                        throw new InternalError(e.toString());
0835:                    }
0836:
0837:                    label.setLocation();
0838:                    builder.loadLocal(a);
0839:                    builder.invoke(doubleToLongBits);
0840:                    builder.loadLocal(b);
0841:                    builder.invoke(doubleToLongBits);
0842:                    builder.math(Opcode.LCMP);
0843:
0844:                    done.setLocation();
0845:                } else if (type == long.class) {
0846:                    builder.loadLocal(a);
0847:                    builder.loadLocal(b);
0848:                    builder.math(Opcode.LCMP);
0849:                } else if (type == int.class) {
0850:                    builder.loadLocal(a);
0851:                    builder.convert(TypeDesc.INT, TypeDesc.LONG);
0852:                    builder.loadLocal(b);
0853:                    builder.convert(TypeDesc.INT, TypeDesc.LONG);
0854:                    builder.math(Opcode.LCMP);
0855:                } else {
0856:                    builder.loadLocal(a);
0857:                    builder.loadLocal(b);
0858:                    builder.math(Opcode.ISUB);
0859:                }
0860:            }
0861:
0862:            private static void generateComparableCompareTo(
0863:                    CodeBuilder builder, Class type, Method compareToMethod,
0864:                    Label goodLabel, Label nextLabel, LocalVariable a,
0865:                    LocalVariable b) {
0866:                if (Comparable.class.isAssignableFrom(type)) {
0867:                    builder.loadLocal(a);
0868:                    builder.loadLocal(b);
0869:                    builder.invoke(compareToMethod);
0870:                    if (goodLabel != null) {
0871:                        builder.branch(goodLabel);
0872:                    }
0873:                } else {
0874:                    // Cast each property to Comparable only if needed.
0875:                    TypeDesc comparableType = TypeDesc
0876:                            .forClass(Comparable.class);
0877:
0878:                    boolean locateGoodLabel = false;
0879:                    if (goodLabel == null) {
0880:                        goodLabel = builder.createLabel();
0881:                        locateGoodLabel = true;
0882:                    }
0883:
0884:                    Label tryStart = builder.createLabel().setLocation();
0885:                    builder.loadLocal(a);
0886:                    builder.checkCast(comparableType);
0887:                    builder.loadLocal(b);
0888:                    builder.checkCast(comparableType);
0889:                    Label tryEnd = builder.createLabel().setLocation();
0890:                    builder.invoke(compareToMethod);
0891:                    builder.branch(goodLabel);
0892:
0893:                    builder.exceptionHandler(tryStart, tryEnd,
0894:                            ClassCastException.class.getName());
0895:                    // One of the properties is not Comparable, so just go to next.
0896:                    // Discard the exception.
0897:                    builder.pop();
0898:                    if (nextLabel == null) {
0899:                        builder.loadConstant(0);
0900:                    } else {
0901:                        builder.branch(nextLabel);
0902:                    }
0903:
0904:                    if (locateGoodLabel) {
0905:                        goodLabel.setLocation();
0906:                    }
0907:                }
0908:            }
0909:
0910:            // A key that uniquely describes the rules of a BeanComparator.
0911:            private static class Rules {
0912:                private BeanComparator[] mRuleParts;
0913:                private int mHashCode;
0914:
0915:                public Rules(BeanComparator bc) {
0916:                    mRuleParts = reduceRules(bc);
0917:
0918:                    // Compute hashCode.
0919:                    int hash = 0;
0920:
0921:                    for (int i = mRuleParts.length - 1; i >= 0; i--) {
0922:                        bc = mRuleParts[i];
0923:                        hash = 31 * hash;
0924:
0925:                        hash += bc.mFlags << 4;
0926:
0927:                        Object obj = bc.mBeanClass;
0928:                        if (obj != null) {
0929:                            hash += obj.hashCode();
0930:                        }
0931:                        obj = bc.mOrderByName;
0932:                        if (obj != null) {
0933:                            hash += obj.hashCode();
0934:                        }
0935:                        obj = bc.mUsingComparator;
0936:                        if (obj != null) {
0937:                            hash += obj.getClass().hashCode();
0938:                        }
0939:                        obj = bc.mCollator;
0940:                        if (obj != null) {
0941:                            hash += obj.getClass().hashCode();
0942:                        }
0943:                    }
0944:
0945:                    mHashCode = hash;
0946:                }
0947:
0948:                public BeanComparator[] getRuleParts() {
0949:                    return mRuleParts;
0950:                }
0951:
0952:                public int hashCode() {
0953:                    return mHashCode;
0954:                }
0955:
0956:                /**
0957:                 * Equality test determines if rules produce an identical
0958:                 * auto-generated Comparator.
0959:                 */
0960:                public boolean equals(Object obj) {
0961:                    if (!(obj instanceof  Rules)) {
0962:                        return false;
0963:                    }
0964:
0965:                    BeanComparator[] ruleParts1 = getRuleParts();
0966:                    BeanComparator[] ruleParts2 = ((Rules) obj).getRuleParts();
0967:
0968:                    if (ruleParts1.length != ruleParts2.length) {
0969:                        return false;
0970:                    }
0971:
0972:                    for (int i = 0; i < ruleParts1.length; i++) {
0973:                        BeanComparator bc1 = ruleParts1[i];
0974:                        BeanComparator bc2 = ruleParts2[i];
0975:
0976:                        if (bc1.mFlags != bc2.mFlags) {
0977:                            return false;
0978:                        }
0979:                        if (!equalTest(bc1.mBeanClass, bc2.mBeanClass)) {
0980:                            return false;
0981:                        }
0982:                        if (!equalTest(bc1.mOrderByName, bc2.mOrderByName)) {
0983:                            return false;
0984:                        }
0985:                        if ((bc1.mUsingComparator == null) != (bc2.mUsingComparator == null)) {
0986:                            return false;
0987:                        }
0988:                        if ((bc1.mCollator == null) != (bc2.mCollator == null)) {
0989:                            return false;
0990:                        }
0991:                    }
0992:
0993:                    return true;
0994:                }
0995:
0996:                private BeanComparator[] reduceRules(BeanComparator bc) {
0997:                    // Reduce the ordering rules by returning BeanComparators
0998:                    // that are at the end of the chain or before an order-by rule.
0999:                    List rules = new ArrayList();
1000:
1001:                    rules.add(bc);
1002:                    String name = bc.mOrderByName;
1003:
1004:                    while ((bc = bc.mParent) != null) {
1005:                        // Don't perform string comparison using equals method.
1006:                        if (name != bc.mOrderByName) {
1007:                            rules.add(bc);
1008:                            name = bc.mOrderByName;
1009:                        }
1010:                    }
1011:
1012:                    int size = rules.size();
1013:                    BeanComparator[] bcs = new BeanComparator[size];
1014:                    // Reverse rules so that they are in forward order.
1015:                    for (int i = 0; i < size; i++) {
1016:                        bcs[size - i - 1] = (BeanComparator) rules.get(i);
1017:                    }
1018:
1019:                    return bcs;
1020:                }
1021:            }
1022:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.