001    /**
002     * Copyright (c) 2000-2013 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.StringUtil;
024    import com.liferay.portal.kernel.util.UnmodifiableList;
025    
026    import java.lang.reflect.Field;
027    
028    import java.util.ArrayList;
029    import java.util.Collection;
030    import java.util.Collections;
031    import java.util.List;
032    import java.util.Map;
033    import java.util.concurrent.ConcurrentHashMap;
034    import java.util.regex.Pattern;
035    
036    /**
037     * @author Prashant Dighe
038     * @author Brian Wing Shun Chan
039     * @author Shuyang Zhou
040     */
041    public class SQLQueryImpl extends QueryImpl implements SQLQuery {
042    
043            public SQLQueryImpl(
044                    SessionImpl sessionImpl, String queryString, boolean strictName) {
045    
046                    super(sessionImpl, queryString, strictName);
047    
048                    sqlQuery = true;
049            }
050    
051            @Override
052            public SQLQuery addEntity(String alias, Class<?> entityClass) {
053                    String columnAliases = null;
054    
055                    try {
056                            String[] columnNames = _getColumns(entityClass);
057    
058                            if (columnNames.length == 0) {
059                                    columnAliases = StringPool.BLANK;
060                            }
061                            else {
062                                    StringBundler sb = new StringBundler(
063                                            columnNames.length * 4 - 1);
064    
065                                    int i = 0;
066    
067                                    for (String column : columnNames) {
068                                            sb.append(alias);
069                                            sb.append(StringPool.PERIOD);
070                                            sb.append(column);
071    
072                                            if ((i + 1) < columnNames.length) {
073                                                    sb.append(StringPool.COMMA_AND_SPACE);
074                                            }
075    
076                                            i++;
077                                    }
078    
079                                    columnAliases = sb.toString();
080                            }
081                    }
082                    catch (Exception e) {
083                            throw new ORMException(e.getMessage());
084                    }
085    
086                    String escapedAlias = Pattern.quote("{" + alias + ".*}");
087    
088                    queryString = queryString.replaceAll(escapedAlias, columnAliases);
089    
090                    this.entityClass = entityClass;
091    
092                    return this;
093            }
094    
095            @Override
096            public SQLQuery addScalar(String columnAlias, Type type) {
097                    columnAlias = StringUtil.toLowerCase(columnAlias);
098    
099                    String q = StringUtil.toLowerCase(queryString);
100    
101                    int fromIndex = q.indexOf("from");
102    
103                    if (fromIndex == -1) {
104                            return this;
105                    }
106    
107                    String selectExpression = q.substring(0, fromIndex);
108    
109                    String[] selectTokens = selectExpression.split(StringPool.COMMA);
110    
111                    for (int pos = 0; pos < selectTokens.length; pos++) {
112                            String s = selectTokens[pos];
113    
114                            if (s.contains(columnAlias)) {
115                                    _scalars.add(pos);
116    
117                                    _scalarTypes.add(type);
118                            }
119                    }
120    
121                    return this;
122            }
123    
124            @Override
125            public SQLQuery addSynchronizedEntityClass(Class<?> entityClass) {
126                    return this;
127            }
128    
129            @Override
130            public SQLQuery addSynchronizedEntityClasses(Class<?>... entityClasses) {
131                    return this;
132            }
133    
134            @Override
135            public SQLQuery addSynchronizedEntityName(String entityName) {
136                    return this;
137            }
138    
139            @Override
140            public SQLQuery addSynchronizedEntityNames(String... entityNames) {
141                    return this;
142            }
143    
144            @Override
145            public SQLQuery addSynchronizedQuerySpace(String querySpace) {
146                    return this;
147            }
148    
149            @Override
150            public SQLQuery addSynchronizedQuerySpaces(String... querySpaces) {
151                    return this;
152            }
153    
154            @Override
155            public List<?> list(boolean copy, boolean unmodifiable)
156                    throws ORMException {
157    
158                    try {
159                            List<?> list = sessionImpl.list(
160                                    queryString, positionalParameterMap, namedParameterMap,
161                                    strictName, firstResult, maxResults, flushModeType,
162                                    lockModeType, sqlQuery, entityClass);
163    
164                            if ((entityClass == null) && !list.isEmpty()) {
165                                    list = _transformList(list);
166                            }
167    
168                            if (unmodifiable) {
169                                    list = new UnmodifiableList<Object>(list);
170                            }
171                            else if (copy) {
172                                    list = ListUtil.copy(list);
173                            }
174    
175                            return list;
176                    }
177                    catch (Exception e) {
178                            throw ExceptionTranslator.translate(e);
179                    }
180            }
181    
182            @Override
183            public Object uniqueResult() throws ORMException {
184                    try {
185                            Object object = sessionImpl.uniqueResult(
186                                    queryString, positionalParameterMap, namedParameterMap,
187                                    strictName, firstResult, maxResults, flushModeType,
188                                    lockModeType, sqlQuery, entityClass);
189    
190                            if (object instanceof Collection<?>) {
191                                    Collection<Object> collection = (Collection<Object>)object;
192    
193                                    if (collection.size() == 1) {
194                                            object = collection.iterator().next();
195                                    }
196                            }
197    
198                            if (_scalars.size() == 1) {
199                                    object = _transformType(object, _scalarTypes.get(0));
200                            }
201    
202                            return object;
203                    }
204                    catch (Exception e) {
205                            throw ExceptionTranslator.translate(e);
206                    }
207            }
208    
209            private String[] _getColumns(Class<?> entityClass) throws Exception {
210                    String[] columns = _entityColumns.get(entityClass);
211    
212                    if (columns != null) {
213                            return columns;
214                    }
215    
216                    Field field = entityClass.getField("TABLE_COLUMNS");
217    
218                    Object[][] tableColumns = (Object[][])field.get(null);
219    
220                    columns = new String[tableColumns.length];
221    
222                    int i = 0;
223    
224                    for (Object[] row : tableColumns) {
225                            String name = (String)row[0];
226    
227                            columns[i++] = StringUtil.toUpperCase(name);
228                    }
229    
230                    _entityColumns.put(entityClass, columns);
231    
232                    return columns;
233            }
234    
235            private List<?> _transformList(List<?> list) throws Exception {
236                    if (!_scalars.isEmpty()) {
237                            Collections.sort(_scalars);
238    
239                            if (list.get(0) instanceof Collection<?>) {
240                                    List<Object> newList = new ArrayList<Object>();
241    
242                                    for (Collection<Object> collection :
243                                                    (List<Collection<Object>>)list) {
244    
245                                            Object[] array = collection.toArray();
246    
247                                            if (_scalars.size() > 1) {
248                                                    Object[] values = new Object[_scalars.size()];
249    
250                                                    for (int i = 0; i < _scalars.size(); i++) {
251                                                            values[i] = array[_scalars.get(i)];
252                                                    }
253    
254                                                    newList.add(values);
255                                            }
256                                            else {
257                                                    newList.add(array[_scalars.get(0)]);
258                                            }
259                                    }
260    
261                                    list = newList;
262                            }
263                            else if (list.get(0) instanceof Object[]) {
264                                    List<Object> newList = new ArrayList<Object>();
265    
266                                    for (Object[] array : (List<Object[]>)list) {
267                                            if (_scalars.size() > 1) {
268                                                    Object[] values = new Object[_scalars.size()];
269    
270                                                    for (int i = 0; i < _scalars.size(); i++) {
271                                                            values[i] = array[_scalars.get(i)];
272                                                    }
273    
274                                                    newList.add(values);
275                                            }
276                                            else {
277                                                    newList.add(array[_scalars.get(0)]);
278                                            }
279                                    }
280    
281                                    list = newList;
282                            }
283                            else if (_scalars.size() == 1) {
284                                    List<Object> newList = new ArrayList<Object>();
285    
286                                    for (Object value : list) {
287                                            value = _transformType(value, _scalarTypes.get(0));
288    
289                                            newList.add(value);
290                                    }
291    
292                                    list = newList;
293                            }
294                    }
295                    else if (list.get(0) instanceof Collection<?>) {
296                            List<Object> newList = new ArrayList<Object>();
297    
298                            for (Collection<Object> collection :
299                                            (List<Collection<Object>>)list) {
300    
301                                    if (collection.size() == 1) {
302                                            newList.add(collection.iterator().next());
303                                    }
304                                    else {
305                                            newList.add(collection.toArray());
306                                    }
307                            }
308    
309                            list = newList;
310                    }
311    
312                    return list;
313            }
314    
315            private Object _transformType(Object object, Type type) {
316                    Object result = object;
317    
318                    if (type.equals(Type.LONG)) {
319                            if (object instanceof Integer) {
320                                    result = new Long((((Integer)object).longValue()));
321                            }
322                    }
323                    else if (type.equals(Type.STRING)) {
324                            result = object.toString();
325                    }
326                    else {
327                            throw new UnsupportedOperationException(
328                                    "Type conversion from " + object.getClass().getName() + " to " +
329                                            type + " is not supported");
330                    }
331    
332                    return result;
333            }
334    
335            private static Map<Class<?>, String[]> _entityColumns =
336                    new ConcurrentHashMap<Class<?>, String[]>();
337    
338            private List<Integer> _scalars = new ArrayList<Integer>();
339            private List<Type> _scalarTypes = new ArrayList<Type>();
340    
341    }