001
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.kernel.util.Validator;
028 import com.liferay.portal.service.ServiceContext;
029 import com.liferay.portal.util.PortalUtil;
030 import com.liferay.portlet.dynamicdatamapping.model.DDMStorageLink;
031 import com.liferay.portlet.dynamicdatamapping.model.DDMStructure;
032 import com.liferay.portlet.dynamicdatamapping.service.DDMStorageLinkLocalServiceUtil;
033 import com.liferay.portlet.dynamicdatamapping.service.DDMStructureLocalServiceUtil;
034 import com.liferay.portlet.dynamicdatamapping.storage.query.ComparisonOperator;
035 import com.liferay.portlet.dynamicdatamapping.storage.query.Condition;
036 import com.liferay.portlet.dynamicdatamapping.storage.query.FieldCondition;
037 import com.liferay.portlet.dynamicdatamapping.storage.query.Junction;
038 import com.liferay.portlet.dynamicdatamapping.storage.query.LogicalOperator;
039 import com.liferay.portlet.dynamicdatamapping.util.DDMUtil;
040 import com.liferay.portlet.expando.NoSuchTableException;
041 import com.liferay.portlet.expando.model.ExpandoColumn;
042 import com.liferay.portlet.expando.model.ExpandoColumnConstants;
043 import com.liferay.portlet.expando.model.ExpandoRow;
044 import com.liferay.portlet.expando.model.ExpandoTable;
045 import com.liferay.portlet.expando.model.ExpandoValue;
046 import com.liferay.portlet.expando.service.ExpandoColumnLocalServiceUtil;
047 import com.liferay.portlet.expando.service.ExpandoRowLocalServiceUtil;
048 import com.liferay.portlet.expando.service.ExpandoTableLocalServiceUtil;
049 import com.liferay.portlet.expando.service.ExpandoValueLocalServiceUtil;
050
051 import java.io.Serializable;
052
053 import java.util.ArrayList;
054 import java.util.Collections;
055 import java.util.Date;
056 import java.util.HashMap;
057 import java.util.Iterator;
058 import java.util.List;
059 import java.util.Locale;
060 import java.util.Map;
061
062 import org.springframework.expression.EvaluationException;
063 import org.springframework.expression.Expression;
064 import org.springframework.expression.ExpressionParser;
065 import org.springframework.expression.ParseException;
066 import org.springframework.expression.spel.standard.SpelExpressionParser;
067 import org.springframework.expression.spel.support.StandardEvaluationContext;
068
069
074 public class ExpandoStorageAdapter extends BaseStorageAdapter {
075
076 @Override
077 protected long doCreate(
078 long companyId, long ddmStructureId, Fields fields,
079 ServiceContext serviceContext)
080 throws Exception {
081
082 ExpandoTable expandoTable = _getExpandoTable(
083 companyId, ddmStructureId, fields);
084
085 ExpandoRow expandoRow = ExpandoRowLocalServiceUtil.addRow(
086 expandoTable.getTableId(), CounterLocalServiceUtil.increment());
087
088 _updateFields(expandoTable, expandoRow.getClassPK(), fields);
089
090 DDMStorageLinkLocalServiceUtil.addStorageLink(
091 expandoTable.getClassNameId(), expandoRow.getRowId(),
092 ddmStructureId, serviceContext);
093
094 return expandoRow.getRowId();
095 }
096
097 @Override
098 protected void doDeleteByClass(long classPK) throws Exception {
099 ExpandoRowLocalServiceUtil.deleteRow(classPK);
100
101 DDMStorageLinkLocalServiceUtil.deleteClassStorageLink(classPK);
102 }
103
104 @Override
105 protected void doDeleteByDDMStructure(long ddmStructureId)
106 throws Exception {
107
108 List<DDMStorageLink> ddmStorageLinks =
109 DDMStorageLinkLocalServiceUtil.getStructureStorageLinks(
110 ddmStructureId);
111
112 for (DDMStorageLink ddmStorageLink : ddmStorageLinks) {
113 ExpandoRowLocalServiceUtil.deleteRow(ddmStorageLink.getClassPK());
114 }
115
116 DDMStorageLinkLocalServiceUtil.deleteStructureStorageLinks(
117 ddmStructureId);
118 }
119
120 @Override
121 protected List<Fields> doGetFieldsListByClasses(
122 long ddmStructureId, long[] classPKs, List<String> fieldNames,
123 OrderByComparator orderByComparator)
124 throws Exception {
125
126 return _doQuery(
127 ddmStructureId, classPKs, fieldNames, null, orderByComparator);
128 }
129
130 @Override
131 protected List<Fields> doGetFieldsListByDDMStructure(
132 long ddmStructureId, List<String> fieldNames,
133 OrderByComparator orderByComparator)
134 throws Exception {
135
136 return _doQuery(ddmStructureId, fieldNames, null, orderByComparator);
137 }
138
139 @Override
140 protected Map<Long, Fields> doGetFieldsMapByClasses(
141 long ddmStructureId, long[] classPKs, List<String> fieldNames)
142 throws Exception {
143
144 return _doQuery(ddmStructureId, classPKs, fieldNames);
145 }
146
147 @Override
148 protected List<Fields> doQuery(
149 long ddmStructureId, List<String> fieldNames, Condition condition,
150 OrderByComparator orderByComparator)
151 throws Exception {
152
153 return _doQuery(
154 ddmStructureId, fieldNames, condition, orderByComparator);
155 }
156
157 @Override
158 protected int doQueryCount(long ddmStructureId, Condition condition)
159 throws Exception {
160
161 Expression expression = null;
162
163 if (condition != null) {
164 expression = _parseExpression(condition);
165 }
166
167 int count = 0;
168
169 long[] expandoRowIds = _getExpandoRowIds(ddmStructureId);
170
171 for (long expandoRowId : expandoRowIds) {
172 List<ExpandoValue> expandoValues =
173 ExpandoValueLocalServiceUtil.getRowValues(expandoRowId);
174
175 if ((expression == null) ||
176 ((expression != null) &&
177 _booleanValueOf(expression, expandoValues))) {
178
179 count++;
180 }
181 }
182
183 return count;
184 }
185
186 @Override
187 protected void doUpdate(
188 long classPK, Fields fields, boolean mergeFields,
189 ServiceContext serviceContext)
190 throws Exception {
191
192 ExpandoRow expandoRow = ExpandoRowLocalServiceUtil.getRow(classPK);
193
194 DDMStorageLink ddmStorageLink =
195 DDMStorageLinkLocalServiceUtil.getClassStorageLink(
196 expandoRow.getRowId());
197
198 ExpandoTable expandoTable = _getExpandoTable(
199 expandoRow.getCompanyId(), ddmStorageLink.getStructureId(), fields);
200
201 if (mergeFields) {
202 fields = DDMUtil.mergeFields(fields, getFields(classPK));
203 }
204
205 ExpandoValueLocalServiceUtil.deleteRowValues(expandoRow.getRowId());
206
207 _updateFields(expandoTable, expandoRow.getClassPK(), fields);
208 }
209
210 private boolean _booleanValueOf(
211 Expression expression, List<ExpandoValue> expandoValues) {
212
213 try {
214 StandardEvaluationContext standardEvaluationContext =
215 new StandardEvaluationContext();
216
217 standardEvaluationContext.setBeanResolver(
218 new ExpandoValueBeanResolver(expandoValues));
219
220 return expression.getValue(
221 standardEvaluationContext, Boolean.class);
222 }
223 catch (EvaluationException ee) {
224 _log.error("Unable to evaluate expression", ee);
225 }
226
227 return false;
228 }
229
230 private void _checkExpandoColumns(ExpandoTable expandoTable, Fields fields)
231 throws PortalException, SystemException {
232
233 for (String name : fields.getNames()) {
234 ExpandoColumn expandoColumn =
235 ExpandoColumnLocalServiceUtil.getColumn(
236 expandoTable.getTableId(), name);
237
238 if (expandoColumn != null) {
239 continue;
240 }
241
242 int type = ExpandoColumnConstants.STRING_LOCALIZED;
243
244 Field field = fields.get(name);
245
246 if (field.isRepeatable()) {
247 type = ExpandoColumnConstants.STRING_ARRAY_LOCALIZED;
248 }
249
250 ExpandoColumnLocalServiceUtil.addColumn(
251 expandoTable.getTableId(), name, type);
252 }
253 }
254
255 private List<Fields> _doQuery(
256 long ddmStructureId, List<String> fieldNames, Condition condition,
257 OrderByComparator orderByComparator)
258 throws Exception {
259
260 return _doQuery(
261 ddmStructureId, _getExpandoRowIds(ddmStructureId), fieldNames,
262 condition, orderByComparator);
263 }
264
265 private Map<Long, Fields> _doQuery(
266 long ddmStructureId, long[] classPKs, List<String> fieldNames)
267 throws Exception {
268
269 Map<Long, Fields> fieldsMap = new HashMap<Long, Fields>();
270
271 List<Fields> fieldsList = _doQuery(
272 ddmStructureId, classPKs, fieldNames, null, null);
273
274 for (int i = 0; i < fieldsList.size(); i++) {
275 Fields fields = fieldsList.get(i);
276
277 fieldsMap.put(classPKs[i], fields);
278 }
279
280 return fieldsMap;
281 }
282
283 private List<Fields> _doQuery(
284 long ddmStructureId, long[] expandoRowIds, List<String> fieldNames,
285 Condition condition, OrderByComparator orderByComparator)
286 throws Exception {
287
288 List<Fields> fieldsList = new ArrayList<Fields>();
289
290 Expression expression = null;
291
292 if (condition != null) {
293 expression = _parseExpression(condition);
294 }
295
296 DDMStructure ddmStructure = DDMStructureLocalServiceUtil.getStructure(
297 ddmStructureId);
298
299 for (long expandoRowId : expandoRowIds) {
300 List<ExpandoValue> expandoValues =
301 ExpandoValueLocalServiceUtil.getRowValues(expandoRowId);
302
303 if ((expression == null) ||
304 ((expression != null) &&
305 _booleanValueOf(expression, expandoValues))) {
306
307 Fields fields = new Fields();
308
309 for (ExpandoValue expandoValue : expandoValues) {
310 ExpandoColumn column = expandoValue.getColumn();
311
312 String fieldName = column.getName();
313
314 if (ddmStructure.hasField(fieldName) &&
315 ((fieldNames == null) ||
316 ((fieldNames != null) &&
317 fieldNames.contains(fieldName)))) {
318
319 Field field = new Field();
320
321 field.setDefaultLocale(expandoValue.getDefaultLocale());
322 field.setDDMStructureId(ddmStructureId);
323 field.setName(fieldName);
324
325 String fieldType = ddmStructure.getFieldDataType(
326 fieldName);
327
328 Map<Locale, List<Serializable>> valuesMap =
329 _getValuesMap(
330 column.getType(), fieldType,
331 expandoValue.getSerializable());
332
333 field.setValuesMap(valuesMap);
334
335 fields.put(field);
336 }
337 }
338
339 fieldsList.add(fields);
340 }
341 }
342
343 if (orderByComparator != null) {
344 Collections.sort(fieldsList, orderByComparator);
345 }
346
347 return fieldsList;
348 }
349
350 private long[] _getExpandoRowIds(long ddmStructureId)
351 throws SystemException {
352
353 List<Long> expandoRowIds = new ArrayList<Long>();
354
355 List<DDMStorageLink> ddmStorageLinks =
356 DDMStorageLinkLocalServiceUtil.getStructureStorageLinks(
357 ddmStructureId);
358
359 for (DDMStorageLink ddmStorageLink : ddmStorageLinks) {
360 expandoRowIds.add(ddmStorageLink.getClassPK());
361 }
362
363 return ArrayUtil.toArray(
364 expandoRowIds.toArray(new Long[expandoRowIds.size()]));
365 }
366
367 private ExpandoTable _getExpandoTable(
368 long companyId, long ddmStructureId, Fields fields)
369 throws PortalException, SystemException {
370
371 ExpandoTable expandoTable = null;
372
373 long classNameId = PortalUtil.getClassNameId(
374 ExpandoStorageAdapter.class.getName());
375
376 try {
377 expandoTable = ExpandoTableLocalServiceUtil.getTable(
378 companyId, classNameId, String.valueOf(ddmStructureId));
379 }
380 catch (NoSuchTableException nste) {
381 expandoTable = ExpandoTableLocalServiceUtil.addTable(
382 companyId, classNameId, String.valueOf(ddmStructureId));
383 }
384
385 _checkExpandoColumns(expandoTable, fields);
386
387 return expandoTable;
388 }
389
390 private Map<Locale, List<Serializable>> _getValuesMap(
391 int columnType, String fieldType, Serializable data) {
392
393 Map<Locale, List<Serializable>> valuesMap =
394 new HashMap<Locale, List<Serializable>>();
395
396 if (columnType == ExpandoColumnConstants.STRING_ARRAY_LOCALIZED) {
397 Map<Locale, String[]> stringArrayMap = (Map<Locale, String[]>)data;
398
399 for (Locale locale : stringArrayMap.keySet()) {
400 String[] value = stringArrayMap.get(locale);
401
402 if (ArrayUtil.isEmpty(value)) {
403 continue;
404 }
405
406 valuesMap.put(locale, _transformValue(fieldType, value));
407 }
408 }
409 else {
410 Map<Locale, String> stringMap = (Map<Locale, String>)data;
411
412 for (Locale locale : stringMap.keySet()) {
413 String value = stringMap.get(locale);
414
415 if (Validator.isNull(value)) {
416 continue;
417 }
418
419 valuesMap.put(locale, _transformValue(fieldType, value));
420 }
421 }
422
423 return valuesMap;
424 }
425
426 private Expression _parseExpression(Condition condition) {
427 String expression = _toExpression(condition);
428
429 try {
430 ExpressionParser expressionParser = new SpelExpressionParser();
431
432 return expressionParser.parseExpression(expression);
433 }
434 catch (ParseException pe) {
435 _log.error("Unable to parse expression " + expression, pe);
436 }
437
438 return null;
439 }
440
441 private String _toExpression(Condition condition) {
442 if (condition.isJunction()) {
443 Junction junction = (Junction)condition;
444
445 return StringPool.OPEN_PARENTHESIS.concat(
446 _toExpression(junction)).concat(StringPool.CLOSE_PARENTHESIS);
447 }
448 else {
449 FieldCondition fieldCondition = (FieldCondition)condition;
450
451 return _toExpression(fieldCondition);
452 }
453 }
454
455 private String _toExpression(FieldCondition fieldCondition) {
456 StringBundler sb = new StringBundler(5);
457
458 sb.append("(@");
459 sb.append(fieldCondition.getName());
460
461 ComparisonOperator comparisonOperator =
462 fieldCondition.getComparisonOperator();
463
464 if (comparisonOperator.equals(ComparisonOperator.LIKE)) {
465 sb.append(".data matches ");
466 }
467 else {
468 sb.append(".data == ");
469 }
470
471 String value = StringUtil.quote(
472 String.valueOf(fieldCondition.getValue()));
473
474 sb.append(value);
475 sb.append(StringPool.CLOSE_PARENTHESIS);
476
477 return sb.toString();
478 }
479
480 private String _toExpression(Junction junction) {
481 StringBundler sb = new StringBundler();
482
483 LogicalOperator logicalOperator = junction.getLogicalOperator();
484
485 Iterator<Condition> itr = junction.iterator();
486
487 while (itr.hasNext()) {
488 Condition condition = itr.next();
489
490 sb.append(_toExpression(condition));
491
492 if (itr.hasNext()) {
493 sb.append(StringPool.SPACE);
494 sb.append(logicalOperator.toString());
495 sb.append(StringPool.SPACE);
496 }
497 }
498
499 return sb.toString();
500 }
501
502 private String[] _toStringArray(String type, Serializable[] values) {
503 String[] stringValues = new String[values.length];
504
505 for (int i = 0; i < values.length; i++) {
506 Serializable serializable = values[i];
507
508 if (FieldConstants.isNumericType(type) && (serializable == null)) {
509 serializable = _NUMERIC_NULL_VALUE;
510 }
511
512 stringValues[i] = String.valueOf(serializable);
513 }
514
515 return stringValues;
516 }
517
518 private List<Serializable> _transformValue(String type, String value) {
519 List<Serializable> serializables = new ArrayList<Serializable>();
520
521 if (FieldConstants.isNumericType(type) &&
522 _NUMERIC_NULL_VALUE.equals(value)) {
523
524 value = null;
525 }
526
527 Serializable serializable = FieldConstants.getSerializable(type, value);
528
529 serializables.add(serializable);
530
531 return serializables;
532 }
533
534 private List<Serializable> _transformValue(String type, String[] values) {
535 List<Serializable> serializables = new ArrayList<Serializable>();
536
537 for (String value : values) {
538 if (FieldConstants.isNumericType(type) &&
539 _NUMERIC_NULL_VALUE.equals(value)) {
540
541 value = null;
542 }
543
544 Serializable serializable = FieldConstants.getSerializable(
545 type, value);
546
547 serializables.add(serializable);
548 }
549
550 return serializables;
551 }
552
553 private void _updateFields(
554 ExpandoTable expandoTable, long classPK, Fields fields)
555 throws PortalException, SystemException {
556
557 Iterator<Field> itr = fields.iterator(true);
558
559 while (itr.hasNext()) {
560 Field field = itr.next();
561
562 Map<Locale, ?> dataMap = null;
563
564 if (field.isRepeatable()) {
565 Map<Locale, String[]> stringArrayMap =
566 new HashMap<Locale, String[]>();
567
568 for (Locale locale : field.getAvailableLocales()) {
569 Serializable value = field.getValue(locale);
570
571 if (value instanceof Date[]) {
572 Date[] dates = (Date[])value;
573
574 String[] values = new String[dates.length];
575
576 for (int i = 0; i < dates.length; i++) {
577 values[i] = String.valueOf(dates[i].getTime());
578 }
579
580 stringArrayMap.put(locale, values);
581 }
582 else {
583 String[] values = _toStringArray(
584 field.getDataType(), (Serializable[])value);
585
586 stringArrayMap.put(locale, values);
587 }
588 }
589
590 dataMap = stringArrayMap;
591 }
592 else {
593 Map<Locale, String> stringMap = new HashMap<Locale, String>();
594
595 for (Locale locale : field.getAvailableLocales()) {
596 Serializable value = field.getValue(locale);
597
598 if (FieldConstants.isNumericType(field.getDataType()) &&
599 (value == null)) {
600
601 value = _NUMERIC_NULL_VALUE;
602 }
603 else if (value instanceof Date) {
604 Date date = (Date)value;
605
606 value = date.getTime();
607 }
608
609 stringMap.put(locale, String.valueOf(value));
610 }
611
612 dataMap = stringMap;
613 }
614
615 ExpandoValueLocalServiceUtil.addValue(
616 expandoTable.getCompanyId(),
617 ExpandoStorageAdapter.class.getName(), expandoTable.getName(),
618 field.getName(), classPK, dataMap, field.getDefaultLocale());
619 }
620 }
621
622 private static final String _NUMERIC_NULL_VALUE = "NUMERIC_NULL_VALUE";
623
624 private static Log _log = LogFactoryUtil.getLog(
625 ExpandoStorageAdapter.class);
626
627 }