001    /*
002     * Copyright 2011 The Kuali Foundation.
003     * 
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     * http://www.opensource.org/licenses/ecl2.php
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.kfs.module.purap.dataaccess.impl;
017    
018    import java.util.Map;
019    
020    import org.apache.commons.lang.StringUtils;
021    import org.apache.ojb.broker.PersistenceBroker;
022    import org.apache.ojb.broker.metadata.ClassDescriptor;
023    import org.apache.ojb.broker.metadata.CollectionDescriptor;
024    import org.apache.ojb.broker.metadata.FieldDescriptor;
025    import org.apache.ojb.broker.platforms.PlatformMySQLImpl;
026    import org.apache.ojb.broker.query.Query;
027    import org.apache.ojb.broker.query.QueryByCriteria;
028    
029    /**
030     * This class improves the default order by in OJB by enforcing consistency between Oracle and MySQLs handling of Null values in a
031     * column. Oracle by default sorts nulls last while MySQL does nulls first (i.e. 1,2,3,null MySQL:null,1,2,3; Oracle:1,2,3,null To
032     * get Mysql to sort correctly we need to negate the field that is being Sorted on (i.e. ORDER BY -column DESC = 1,2,3,null while
033     * ORDER BY column DESC = 3,2,1,null) the oracle default for ORDER BY is "NULLS LAST" which the above MySQL tweak should make it
034     * like. This could be improved to pass in nullsFirst to decide which way to display but that would be beyond what ojb currently
035     * does
036     */
037    public class PurapItemQueryCustomizer extends KualiQueryCustomizerDefaultImpl {
038        protected static final String MYSQL_NEGATION = "-";
039        public final static String ORDER_BY_FIELD = "orderByField.";
040        public final static String ASCENDING = "ASC";
041        public final static String DESCENDING = "DESC";
042    
043        /**
044         * In addition to what the referenced method does, this also fixes a mysql order by issue (see class comments)
045         * @see org.apache.ojb.broker.accesslayer.QueryCustomizerDefaultImpl#customizeQuery(java.lang.Object,
046         *      org.apache.ojb.broker.PersistenceBroker, org.apache.ojb.broker.metadata.CollectionDescriptor,
047         *      org.apache.ojb.broker.query.QueryByCriteria)
048         */
049        @Override
050        public Query customizeQuery(Object anObject, PersistenceBroker broker, CollectionDescriptor cod, QueryByCriteria query) {
051            boolean platformMySQL = broker.serviceSqlGenerator().getPlatform() instanceof PlatformMySQLImpl;
052    
053            Map<String, String> attributes = getAttributes();
054            for (String attributeName : attributes.keySet()) {
055                if (!attributeName.startsWith(ORDER_BY_FIELD)) {
056                    continue;
057                }
058    
059                String fieldName = attributeName.substring(ORDER_BY_FIELD.length());
060                ClassDescriptor itemClassDescriptor = broker.getClassDescriptor(cod.getItemClass());
061                FieldDescriptor orderByFieldDescriptior = itemClassDescriptor.getFieldDescriptorByName(fieldName);
062    
063                // the column to sort on derived from the property name
064                String orderByColumnName = orderByFieldDescriptior.getColumnName();
065    
066                // ascending or descending
067                String fieldValue = attributes.get(attributeName);
068                boolean ascending = (StringUtils.equals(fieldValue, ASCENDING));
069                // throw an error if not ascending or descending
070                if (!ascending && StringUtils.equals(fieldValue, DESCENDING)) {
071                    throw new RuntimeException("neither ASC nor DESC was specified in ojb file for " + fieldName);
072                }
073    
074                if (platformMySQL) {
075                    // by negating the column name in MySQL we can get nulls last (ascending or descending)
076                    String mysqlPrefix = (ascending) ? MYSQL_NEGATION : "";
077                    query.addOrderBy(mysqlPrefix + orderByColumnName, false);
078                }
079                else {
080                    query.addOrderBy(orderByColumnName, ascending);
081                }
082            }
083            return query;
084        }
085    }