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