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.portlet.dynamicdatamapping.storage;
016    
017    import com.liferay.counter.service.CounterLocalServiceUtil;
018    import com.liferay.portal.kernel.exception.PortalException;
019    import com.liferay.portal.kernel.exception.SystemException;
020    import com.liferay.portal.kernel.log.Log;
021    import com.liferay.portal.kernel.log.LogFactoryUtil;
022    import com.liferay.portal.kernel.util.ArrayUtil;
023    import com.liferay.portal.kernel.util.OrderByComparator;
024    import com.liferay.portal.kernel.util.StringBundler;
025    import com.liferay.portal.kernel.util.StringPool;
026    import com.liferay.portal.kernel.util.StringUtil;
027    import com.liferay.portal.service.ServiceContext;
028    import com.liferay.portal.util.PortalUtil;
029    import com.liferay.portlet.dynamicdatamapping.model.DDMStorageLink;
030    import com.liferay.portlet.dynamicdatamapping.model.DDMStructure;
031    import com.liferay.portlet.dynamicdatamapping.service.DDMStorageLinkLocalServiceUtil;
032    import com.liferay.portlet.dynamicdatamapping.service.DDMStructureLocalServiceUtil;
033    import com.liferay.portlet.dynamicdatamapping.storage.query.ComparisonOperator;
034    import com.liferay.portlet.dynamicdatamapping.storage.query.Condition;
035    import com.liferay.portlet.dynamicdatamapping.storage.query.FieldCondition;
036    import com.liferay.portlet.dynamicdatamapping.storage.query.Junction;
037    import com.liferay.portlet.dynamicdatamapping.storage.query.LogicalOperator;
038    import com.liferay.portlet.expando.NoSuchTableException;
039    import com.liferay.portlet.expando.model.ExpandoColumn;
040    import com.liferay.portlet.expando.model.ExpandoColumnConstants;
041    import com.liferay.portlet.expando.model.ExpandoRow;
042    import com.liferay.portlet.expando.model.ExpandoTable;
043    import com.liferay.portlet.expando.model.ExpandoValue;
044    import com.liferay.portlet.expando.service.ExpandoColumnLocalServiceUtil;
045    import com.liferay.portlet.expando.service.ExpandoRowLocalServiceUtil;
046    import com.liferay.portlet.expando.service.ExpandoTableLocalServiceUtil;
047    import com.liferay.portlet.expando.service.ExpandoValueLocalServiceUtil;
048    
049    import java.io.Serializable;
050    
051    import java.util.ArrayList;
052    import java.util.Collections;
053    import java.util.HashMap;
054    import java.util.Iterator;
055    import java.util.List;
056    import java.util.Map;
057    
058    import org.springframework.expression.EvaluationException;
059    import org.springframework.expression.Expression;
060    import org.springframework.expression.ExpressionParser;
061    import org.springframework.expression.ParseException;
062    import org.springframework.expression.spel.standard.SpelExpressionParser;
063    import org.springframework.expression.spel.support.StandardEvaluationContext;
064    
065    /**
066     * @author Eduardo Lundgren
067     * @author Brian Wing Shun Chan
068     * @author Marcellus Tavares
069     */
070    public class ExpandoStorageAdapter extends BaseStorageAdapter {
071    
072            @Override
073            protected long doCreate(
074                            long companyId, long ddmStructureId, Fields fields,
075                            ServiceContext serviceContext)
076                    throws Exception {
077    
078                    ExpandoTable expandoTable = _getExpandoTable(
079                            companyId, ddmStructureId, fields);
080    
081                    ExpandoRow expandoRow = ExpandoRowLocalServiceUtil.addRow(
082                            expandoTable.getTableId(), CounterLocalServiceUtil.increment());
083    
084                    _updateFields(expandoTable, expandoRow.getClassPK(), fields);
085    
086                    DDMStorageLinkLocalServiceUtil.addStorageLink(
087                            expandoTable.getClassNameId(), expandoRow.getRowId(),
088                            ddmStructureId, serviceContext);
089    
090                    return expandoRow.getRowId();
091            }
092    
093            @Override
094            protected void doDeleteByClass(long classPK) throws Exception {
095                    ExpandoRowLocalServiceUtil.deleteRow(classPK);
096    
097                    DDMStorageLinkLocalServiceUtil.deleteClassStorageLink(classPK);
098            }
099    
100            @Override
101            protected void doDeleteByDDMStructure(long ddmStructureId)
102                    throws Exception {
103    
104                    List<DDMStorageLink> ddmStorageLinks =
105                            DDMStorageLinkLocalServiceUtil.getStructureStorageLinks(
106                                    ddmStructureId);
107    
108                    for (DDMStorageLink ddmStorageLink : ddmStorageLinks) {
109                            ExpandoRowLocalServiceUtil.deleteRow(ddmStorageLink.getClassPK());
110                    }
111    
112                    DDMStorageLinkLocalServiceUtil.deleteStructureStorageLinks(
113                            ddmStructureId);
114            }
115    
116            @Override
117            protected List<Fields> doGetFieldsListByClasses(
118                            long ddmStructureId, long[] classPKs, List<String> fieldNames,
119                            OrderByComparator orderByComparator)
120                    throws Exception {
121    
122                    return _doQuery(
123                            ddmStructureId, classPKs, fieldNames, null, orderByComparator);
124            }
125    
126            @Override
127            protected List<Fields> doGetFieldsListByDDMStructure(
128                            long ddmStructureId, List<String> fieldNames,
129                            OrderByComparator orderByComparator)
130                    throws Exception {
131    
132                    return _doQuery(ddmStructureId, fieldNames, null, orderByComparator);
133            }
134    
135            @Override
136            protected Map<Long, Fields> doGetFieldsMapByClasses(
137                            long ddmStructureId, long[] classPKs, List<String> fieldNames)
138                    throws Exception {
139    
140                    return _doQuery(ddmStructureId, classPKs, fieldNames);
141            }
142    
143            @Override
144            protected List<Fields> doQuery(
145                            long ddmStructureId, List<String> fieldNames, Condition condition,
146                            OrderByComparator orderByComparator)
147                    throws Exception {
148    
149                    return _doQuery(
150                            ddmStructureId, fieldNames, condition, orderByComparator);
151            }
152    
153            @Override
154            protected int doQueryCount(long ddmStructureId, Condition condition)
155                    throws Exception {
156    
157                    Expression expression = null;
158    
159                    if (condition != null) {
160                            expression = _parseExpression(condition);
161                    }
162    
163                    int count = 0;
164    
165                    long[] expandoRowIds = _getExpandoRowIds(ddmStructureId);
166    
167                    for (long expandoRowId : expandoRowIds) {
168                            List<ExpandoValue> expandoValues =
169                                    ExpandoValueLocalServiceUtil.getRowValues(expandoRowId);
170    
171                            if ((expression == null) ||
172                                    ((expression != null) &&
173                                     _booleanValueOf(expression, expandoValues))) {
174    
175                                    count++;
176                            }
177                    }
178    
179                    return count;
180            }
181    
182            @Override
183            protected void doUpdate(
184                            long classPK, Fields fields, boolean mergeFields,
185                            ServiceContext serviceContext)
186                    throws Exception {
187    
188                    ExpandoRow expandoRow = ExpandoRowLocalServiceUtil.getRow(classPK);
189    
190                    DDMStorageLink ddmStorageLink =
191                            DDMStorageLinkLocalServiceUtil.getClassStorageLink(
192                                    expandoRow.getRowId());
193    
194                    ExpandoTable expandoTable = _getExpandoTable(
195                            expandoRow.getCompanyId(), ddmStorageLink.getStructureId(), fields);
196    
197                    List<ExpandoColumn> expandoColumns =
198                            ExpandoColumnLocalServiceUtil.getColumns(expandoTable.getTableId());
199    
200                    if (!mergeFields) {
201                            for (ExpandoColumn expandoColumn : expandoColumns) {
202                                    if (!fields.contains(expandoColumn.getName())) {
203                                            ExpandoValueLocalServiceUtil.deleteValue(
204                                                    expandoColumn.getColumnId(), expandoRow.getRowId());
205                                    }
206                            }
207                    }
208    
209                    _updateFields(expandoTable, expandoRow.getClassPK(), fields);
210            }
211    
212            private boolean _booleanValueOf(
213                    Expression expression, List<ExpandoValue> expandoValues) {
214    
215                    try {
216                            StandardEvaluationContext standardEvaluationContext =
217                                    new StandardEvaluationContext();
218    
219                            standardEvaluationContext.setBeanResolver(
220                                    new ExpandoValueBeanResolver(expandoValues));
221    
222                            return expression.getValue(
223                                    standardEvaluationContext, Boolean.class);
224                    }
225                    catch (EvaluationException ee) {
226                            _log.error("Unable to evaluate expression", ee);
227                    }
228    
229                    return false;
230            }
231    
232            private void _checkExpandoColumns(
233                            long ddmStructureId, ExpandoTable expandoTable, Fields fields)
234                    throws PortalException, SystemException {
235    
236                    for (String name : fields.getNames()) {
237                            ExpandoColumn expandoColumn =
238                                    ExpandoColumnLocalServiceUtil.getColumn(
239                                            expandoTable.getTableId(), name);
240    
241                            if (expandoColumn != null) {
242                                    continue;
243                            }
244    
245                            int type = _getExpandoColumnType(ddmStructureId, name);
246    
247                            ExpandoColumnLocalServiceUtil.addColumn(
248                                    expandoTable.getTableId(), name, type);
249                    }
250            }
251    
252            private List<Fields> _doQuery(
253                            long ddmStructureId, List<String> fieldNames, Condition condition,
254                            OrderByComparator orderByComparator)
255                    throws Exception {
256    
257                    return _doQuery(
258                            ddmStructureId, _getExpandoRowIds(ddmStructureId), fieldNames,
259                            condition, orderByComparator);
260            }
261    
262            private Map<Long, Fields> _doQuery(
263                            long ddmStructureId, long[] classPKs, List<String> fieldNames)
264                    throws Exception {
265    
266                    Map<Long, Fields> fieldsMap = new HashMap<Long, Fields>();
267    
268                    List<Fields> fieldsList = _doQuery(
269                            ddmStructureId, classPKs, fieldNames, null, null);
270    
271                    for (int i = 0; i < fieldsList.size(); i++) {
272                            Fields fields = fieldsList.get(i);
273    
274                            fieldsMap.put(classPKs[i], fields);
275                    }
276    
277                    return fieldsMap;
278            }
279    
280            private List<Fields> _doQuery(
281                            long ddmStructureId, long[] expandoRowIds, List<String> fieldNames,
282                            Condition condition, OrderByComparator orderByComparator)
283                    throws Exception {
284    
285                    List<Fields> fieldsList = new ArrayList<Fields>();
286    
287                    Expression expression = null;
288    
289                    if (condition != null) {
290                            expression = _parseExpression(condition);
291                    }
292    
293                    DDMStructure ddmStructure = DDMStructureLocalServiceUtil.getStructure(
294                            ddmStructureId);
295    
296                    for (long expandoRowId : expandoRowIds) {
297                            List<ExpandoValue> expandoValues =
298                                    ExpandoValueLocalServiceUtil.getRowValues(expandoRowId);
299    
300                            if ((expression == null) ||
301                                    ((expression != null) &&
302                                     _booleanValueOf(expression, expandoValues))) {
303    
304                                    Fields fields = new Fields();
305    
306                                    for (ExpandoValue expandoValue : expandoValues) {
307                                            ExpandoColumn column = expandoValue.getColumn();
308    
309                                            String fieldName = column.getName();
310                                            Serializable fieldValue = expandoValue.getSerializable();
311    
312                                            if (ddmStructure.hasField(fieldName) &&
313                                                    ((fieldNames == null) ||
314                                                     ((fieldNames != null) &&
315                                                      fieldNames.contains(fieldName)))) {
316    
317                                                    Field field = new Field(
318                                                            ddmStructureId, fieldName, fieldValue);
319    
320                                                    fields.put(field);
321                                            }
322                                    }
323    
324                                    fieldsList.add(fields);
325                            }
326                    }
327    
328                    if (orderByComparator != null) {
329                            Collections.sort(fieldsList, orderByComparator);
330                    }
331    
332                    return fieldsList;
333            }
334    
335            private int _getExpandoColumnType(long ddmStructureId, String name)
336                    throws PortalException, SystemException {
337    
338                    DDMStructure ddmStructure = DDMStructureLocalServiceUtil.getStructure(
339                            ddmStructureId);
340    
341                    String fieldDataType = ddmStructure.getFieldDataType(name);
342    
343                    if (fieldDataType.equals(FieldConstants.BOOLEAN)) {
344                            return ExpandoColumnConstants.BOOLEAN;
345                    }
346                    else if (fieldDataType.equals(FieldConstants.DATE)) {
347                            return ExpandoColumnConstants.DATE;
348                    }
349                    else if (fieldDataType.equals(FieldConstants.DOUBLE)) {
350                            return ExpandoColumnConstants.DOUBLE;
351                    }
352                    else if (fieldDataType.equals(FieldConstants.FLOAT)) {
353                            return ExpandoColumnConstants.FLOAT;
354                    }
355                    else if (fieldDataType.equals(FieldConstants.INTEGER)) {
356                            return ExpandoColumnConstants.INTEGER;
357                    }
358                    else if (fieldDataType.equals(FieldConstants.LONG)) {
359                            return ExpandoColumnConstants.LONG;
360                    }
361                    else if (fieldDataType.equals(FieldConstants.NUMBER)) {
362                            return ExpandoColumnConstants.NUMBER;
363                    }
364                    else if (fieldDataType.equals(FieldConstants.SHORT)) {
365                            return ExpandoColumnConstants.SHORT;
366                    }
367                    else {
368                            return ExpandoColumnConstants.STRING;
369                    }
370            }
371    
372            private long[] _getExpandoRowIds(long ddmStructureId)
373                    throws SystemException {
374    
375                    List<Long> expandoRowIds = new ArrayList<Long>();
376    
377                    List<DDMStorageLink> ddmStorageLinks =
378                            DDMStorageLinkLocalServiceUtil.getStructureStorageLinks(
379                                    ddmStructureId);
380    
381                    for (DDMStorageLink ddmStorageLink : ddmStorageLinks) {
382                            expandoRowIds.add(ddmStorageLink.getClassPK());
383                    }
384    
385                    return ArrayUtil.toArray(
386                            expandoRowIds.toArray(new Long[expandoRowIds.size()]));
387            }
388    
389            private ExpandoTable _getExpandoTable(
390                            long companyId, long ddmStructureId, Fields fields)
391                    throws PortalException, SystemException {
392    
393                    ExpandoTable expandoTable = null;
394    
395                    long classNameId = PortalUtil.getClassNameId(
396                            ExpandoStorageAdapter.class.getName());
397    
398                    try {
399                            expandoTable = ExpandoTableLocalServiceUtil.getTable(
400                                    companyId, classNameId, String.valueOf(ddmStructureId));
401                    }
402                    catch (NoSuchTableException nste) {
403                            expandoTable = ExpandoTableLocalServiceUtil.addTable(
404                                    companyId, classNameId, String.valueOf(ddmStructureId));
405                    }
406    
407                    _checkExpandoColumns(ddmStructureId, expandoTable, fields);
408    
409                    return expandoTable;
410            }
411    
412            private Expression _parseExpression(Condition condition) {
413                    String expression = _toExpression(condition);
414    
415                    try {
416                            ExpressionParser expressionParser = new SpelExpressionParser();
417    
418                            return expressionParser.parseExpression(expression);
419                    }
420                    catch (ParseException pe) {
421                            _log.error("Unable to parse expression " + expression, pe);
422                    }
423    
424                    return null;
425            }
426    
427            private String _toExpression(Condition condition) {
428                    if (condition.isJunction()) {
429                            Junction junction = (Junction)condition;
430    
431                            return StringPool.OPEN_PARENTHESIS.concat(
432                                    _toExpression(junction)).concat(StringPool.CLOSE_PARENTHESIS);
433                    }
434                    else {
435                            FieldCondition fieldCondition = (FieldCondition)condition;
436    
437                            return _toExpression(fieldCondition);
438                    }
439            }
440    
441            private String _toExpression(FieldCondition fieldCondition) {
442                    StringBundler sb = new StringBundler(5);
443    
444                    sb.append("(@");
445                    sb.append(fieldCondition.getName());
446    
447                    ComparisonOperator comparisonOperator =
448                            fieldCondition.getComparisonOperator();
449    
450                    if (comparisonOperator.equals(ComparisonOperator.LIKE)) {
451                            sb.append(".data matches ");
452                    }
453                    else {
454                            sb.append(".data == ");
455                    }
456    
457                    String value = StringUtil.quote(
458                            String.valueOf(fieldCondition.getValue()));
459    
460                    sb.append(value);
461                    sb.append(StringPool.CLOSE_PARENTHESIS);
462    
463                    return sb.toString();
464            }
465    
466            private String _toExpression(Junction junction) {
467                    StringBundler sb = new StringBundler();
468    
469                    LogicalOperator logicalOperator = junction.getLogicalOperator();
470    
471                    Iterator<Condition> itr = junction.iterator();
472    
473                    while (itr.hasNext()) {
474                            Condition condition = itr.next();
475    
476                            sb.append(_toExpression(condition));
477    
478                            if (itr.hasNext()) {
479                                    sb.append(StringPool.SPACE);
480                                    sb.append(logicalOperator.toString());
481                                    sb.append(StringPool.SPACE);
482                            }
483                    }
484    
485                    return sb.toString();
486            }
487    
488            private void _updateFields(
489                            ExpandoTable expandoTable, long classPK, Fields fields)
490                    throws PortalException, SystemException {
491    
492                    Iterator<Field> itr = fields.iterator();
493    
494                    while (itr.hasNext()) {
495                            Field field = itr.next();
496    
497                            ExpandoValueLocalServiceUtil.addValue(
498                                    expandoTable.getCompanyId(),
499                                    ExpandoStorageAdapter.class.getName(), expandoTable.getName(),
500                                    field.getName(), classPK, field.getValue());
501                    }
502            }
503    
504            private static Log _log = LogFactoryUtil.getLog(
505                    ExpandoStorageAdapter.class);
506    
507    }