001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.dao.orm.jpa;
016    
017    import com.liferay.portal.kernel.dao.orm.ORMException;
018    import com.liferay.portal.kernel.dao.orm.SQLQuery;
019    import com.liferay.portal.kernel.dao.orm.Type;
020    import com.liferay.portal.kernel.util.ListUtil;
021    import com.liferay.portal.kernel.util.StringBundler;
022    import com.liferay.portal.kernel.util.StringPool;
023    import com.liferay.portal.kernel.util.UnmodifiableList;
024    
025    import java.lang.reflect.Field;
026    
027    import java.util.ArrayList;
028    import java.util.Collection;
029    import java.util.Collections;
030    import java.util.List;
031    import java.util.Map;
032    import java.util.concurrent.ConcurrentHashMap;
033    import java.util.regex.Pattern;
034    
035    /**
036     * @author Prashant Dighe
037     * @author Brian Wing Shun Chan
038     */
039    public class SQLQueryImpl extends QueryImpl implements SQLQuery {
040    
041            public SQLQueryImpl(SessionImpl sessionImpl, String queryString) {
042                    super(sessionImpl, queryString);
043    
044                    sqlQuery = true;
045            }
046    
047            public SQLQuery addEntity(String alias, Class<?> entityClass) {
048                    String columnAliases = null;
049    
050                    try {
051                            String[] columnNames = _getColumns(entityClass);
052    
053                            if (columnNames.length == 0) {
054                                    columnAliases = StringPool.BLANK;
055                            }
056                            else {
057                                    StringBundler sb = new StringBundler(
058                                            columnNames.length * 4 - 1);
059    
060                                    int i = 0;
061    
062                                    for (String column : columnNames) {
063                                            sb.append(alias);
064                                            sb.append(StringPool.PERIOD);
065                                            sb.append(column);
066    
067                                            if ((i + 1) < columnNames.length) {
068                                                    sb.append(StringPool.COMMA_AND_SPACE);
069                                            }
070    
071                                            i++;
072                                    }
073    
074                                    columnAliases = sb.toString();
075                            }
076                    }
077                    catch (Exception e) {
078                            throw new ORMException(e.getMessage());
079                    }
080    
081                    String escapedAlias = Pattern.quote("{" + alias + ".*}");
082    
083                    queryString = queryString.replaceAll(escapedAlias, columnAliases);
084    
085                    this.entityClass = entityClass;
086    
087                    return this;
088            }
089    
090            public SQLQuery addScalar(String columnAlias, Type type) {
091                    columnAlias = columnAlias.toLowerCase();
092    
093                    String q = queryString.toLowerCase();
094    
095                    int fromIndex = q.indexOf("from");
096    
097                    if (fromIndex == -1) {
098                            return this;
099                    }
100    
101                    String selectExpression = q.substring(0, fromIndex);
102    
103                    String[] selectTokens = selectExpression.split(StringPool.COMMA);
104    
105                    for (int pos = 0; pos < selectTokens.length; pos++) {
106                            String s = selectTokens[pos];
107    
108                            if (s.indexOf(columnAlias) != -1) {
109                                    _scalars.add(pos);
110    
111                                    _scalarTypes.add(type);
112                            }
113                    }
114    
115                    return this;
116            }
117    
118            public List<?> list(boolean unmodifiable) throws ORMException {
119                    try {
120                            List<?> list = sessionImpl.list(
121                                    queryString, parameterMap, firstResult, maxResults,
122                                    flushModeType, sqlQuery, entityClass);
123    
124                            if ((entityClass == null) && !list.isEmpty()) {
125                                    list = _transformList(list);
126                            }
127    
128                            if (unmodifiable) {
129                                    return new UnmodifiableList<Object>(list);
130                            }
131                            else {
132                                    return ListUtil.copy(list);
133                            }
134                    }
135                    catch (Exception e) {
136                            throw ExceptionTranslator.translate(e);
137                    }
138            }
139    
140            public Object uniqueResult() throws ORMException {
141                    try {
142                            Object object =  sessionImpl.uniqueResult(
143                                    queryString, parameterMap, firstResult, maxResults,
144                                    flushModeType, sqlQuery, entityClass);
145    
146                            if (object instanceof Collection<?>) {
147                                    Collection<Object> collection = (Collection<Object>)object;
148    
149                                    if (collection.size() == 1) {
150                                            object = collection.iterator().next();
151                                    }
152                            }
153    
154                            if (_scalars.size() == 1) {
155                                    object = _transformType(object, _scalarTypes.get(0));
156                            }
157    
158                            return object;
159                    }
160                    catch (Exception e) {
161                            throw ExceptionTranslator.translate(e);
162                    }
163            }
164    
165            private String[] _getColumns(Class<?> entityClass) throws Exception {
166                    String[] columns = _entityColumns.get(entityClass);
167    
168                    if (columns != null) {
169                            return columns;
170                    }
171    
172                    Field field = entityClass.getField("TABLE_COLUMNS");
173    
174                    Object[][] tableColumns = (Object[][])field.get(null);
175    
176                    columns = new String[tableColumns.length];
177    
178                    int i = 0;
179    
180                    for (Object[] row : tableColumns) {
181                            String name = (String)row[0];
182    
183                            columns[i++] = name.toUpperCase();
184                    }
185    
186                    _entityColumns.put(entityClass, columns);
187    
188                    return columns;
189            }
190    
191            private List<?> _transformList(List<?> list) throws Exception {
192                    if (!_scalars.isEmpty()) {
193                            Collections.sort(_scalars);
194    
195                            if (list.get(0) instanceof Collection<?>) {
196                                    List<Object> newList = new ArrayList<Object>();
197    
198                                    for (Collection<Object> collection :
199                                                    (List<Collection<Object>>)list) {
200    
201                                            Object[] array = collection.toArray();
202    
203                                            if (_scalars.size() > 1) {
204                                                    Object[] values = new Object[_scalars.size()];
205    
206                                                    for (int i = 0; i < _scalars.size(); i++) {
207                                                            values[i] = array[_scalars.get(i)];
208                                                    }
209    
210                                                    newList.add(values);
211                                            }
212                                            else {
213                                                    newList.add(array[_scalars.get(0)]);
214                                            }
215                                    }
216    
217                                    list = newList;
218                            }
219                            else if (list.get(0) instanceof Object[]) {
220                                    List<Object> newList = new ArrayList<Object>();
221    
222                                    for (Object[] array : (List<Object[]>)list) {
223                                            if (_scalars.size() > 1) {
224                                                    Object[] values = new Object[_scalars.size()];
225    
226                                                    for (int i = 0; i < _scalars.size(); i++) {
227                                                            values[i] = array[_scalars.get(i)];
228                                                    }
229    
230                                                    newList.add(values);
231                                            }
232                                            else {
233                                                    newList.add(array[_scalars.get(0)]);
234                                            }
235                                    }
236    
237                                    list = newList;
238                            }
239                            else if ((_scalars.size() == 1)) {
240                                    List<Object> newList = new ArrayList<Object>();
241    
242                                    for (Object value : list) {
243                                            value = _transformType(value, _scalarTypes.get(0));
244    
245                                            newList.add(value);
246                                    }
247    
248                                    list = newList;
249                            }
250                    }
251                    else if (list.get(0) instanceof Collection<?>) {
252                            List<Object> newList = new ArrayList<Object>();
253    
254                            for (Collection<Object> collection :
255                                            (List<Collection<Object>>)list) {
256    
257                                    if (collection.size() == 1) {
258                                            newList.add(collection.iterator().next());
259                                    }
260                                    else {
261                                            newList.add(collection.toArray());
262                                    }
263                            }
264    
265                            list = newList;
266                    }
267    
268                    return list;
269            }
270    
271            private Object _transformType(Object object, Type type) {
272                    Object result = object;
273    
274                    if (type.equals(Type.LONG)) {
275                            if (object instanceof Integer) {
276                                    result = new Long((((Integer)object).longValue()));
277                            }
278                    }
279                    else if (type.equals(Type.STRING)) {
280                            result = object.toString();
281                    }
282                    else {
283                            throw new UnsupportedOperationException(
284                                    "Type conversion from " + object.getClass().getName() + " to " +
285                                            type + " is not supported");
286                    }
287    
288                    return result;
289            }
290    
291            private static Map<Class<?>, String[]> _entityColumns =
292                    new ConcurrentHashMap<Class<?>, String[]>();
293    
294            private List<Integer> _scalars = new ArrayList<Integer>();
295            private List<Type> _scalarTypes = new ArrayList<Type>();
296    
297    }