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