001    /**
002     * Copyright (c) 2000-2011 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.portal.kernel.util.ArrayUtil;
018    import com.liferay.portal.kernel.util.OrderByComparator;
019    import com.liferay.portal.kernel.util.StringBundler;
020    import com.liferay.portal.kernel.util.StringPool;
021    import com.liferay.portal.kernel.util.StringUtil;
022    import com.liferay.portal.kernel.xml.Document;
023    import com.liferay.portal.kernel.xml.Element;
024    import com.liferay.portal.kernel.xml.Node;
025    import com.liferay.portal.kernel.xml.SAXReaderUtil;
026    import com.liferay.portal.kernel.xml.XPath;
027    import com.liferay.portal.service.ServiceContext;
028    import com.liferay.portal.util.PortalUtil;
029    import com.liferay.portlet.dynamicdatamapping.model.DDMContent;
030    import com.liferay.portlet.dynamicdatamapping.model.DDMStorageLink;
031    import com.liferay.portlet.dynamicdatamapping.model.DDMStructure;
032    import com.liferay.portlet.dynamicdatamapping.service.DDMContentLocalServiceUtil;
033    import com.liferay.portlet.dynamicdatamapping.service.DDMStorageLinkLocalServiceUtil;
034    import com.liferay.portlet.dynamicdatamapping.service.DDMStructureLocalServiceUtil;
035    import com.liferay.portlet.dynamicdatamapping.storage.query.ComparisonOperator;
036    import com.liferay.portlet.dynamicdatamapping.storage.query.Condition;
037    import com.liferay.portlet.dynamicdatamapping.storage.query.FieldCondition;
038    import com.liferay.portlet.dynamicdatamapping.storage.query.FieldConditionImpl;
039    import com.liferay.portlet.dynamicdatamapping.storage.query.Junction;
040    import com.liferay.portlet.dynamicdatamapping.storage.query.LogicalOperator;
041    
042    import java.io.Serializable;
043    
044    import java.util.ArrayList;
045    import java.util.Collections;
046    import java.util.Date;
047    import java.util.HashMap;
048    import java.util.Iterator;
049    import java.util.List;
050    import java.util.Map;
051    
052    /**
053     * @author Eduardo Lundgren
054     * @author Brian Wing Shun Chan
055     */
056    public class XMLStorageAdapter extends BaseStorageAdapter {
057    
058            @Override
059            protected long doCreate(
060                            long companyId, long ddmStructureId, Fields fields,
061                            ServiceContext serviceContext)
062                    throws Exception {
063    
064                    long classNameId = PortalUtil.getClassNameId(
065                            DDMContent.class.getName());
066    
067                    Document document = SAXReaderUtil.createDocument();
068    
069                    Element rootElement = document.addElement("root");
070    
071                    Iterator<Field> itr = fields.iterator();
072    
073                    while (itr.hasNext()) {
074                            Field field = itr.next();
075    
076                            Object value = field.getValue();
077    
078                            if (value instanceof Date) {
079                                    Date valueDate = (Date)value;
080    
081                                    value = valueDate.getTime();
082                            }
083    
084                            _appendField(rootElement, field.getName(), String.valueOf(value));
085                    }
086    
087                    DDMContent ddmContent = DDMContentLocalServiceUtil.addContent(
088                            serviceContext.getUserId(), serviceContext.getScopeGroupId(),
089                            DDMStorageLink.class.getName(), null, document.formattedString(),
090                            serviceContext);
091    
092                    DDMStorageLinkLocalServiceUtil.addStorageLink(
093                            classNameId, ddmContent.getPrimaryKey(), ddmStructureId,
094                            serviceContext);
095    
096                    return ddmContent.getPrimaryKey();
097            }
098    
099            @Override
100            protected void doDeleteByClass(long classPK) throws Exception {
101                    DDMContentLocalServiceUtil.deleteDDMContent(classPK);
102    
103                    DDMStorageLinkLocalServiceUtil.deleteClassStorageLink(classPK);
104            }
105    
106            @Override
107            protected void doDeleteByDDMStructure(long ddmStructureId)
108                    throws Exception {
109    
110                    List<DDMStorageLink> ddmStorageLinks =
111                            DDMStorageLinkLocalServiceUtil.getStructureStorageLinks(
112                                    ddmStructureId);
113    
114                    for (DDMStorageLink ddmStorageLink : ddmStorageLinks) {
115                            DDMContentLocalServiceUtil.deleteDDMContent(
116                                    ddmStorageLink.getClassPK());
117                    }
118    
119                    DDMStorageLinkLocalServiceUtil.deleteStructureStorageLinks(
120                            ddmStructureId);
121            }
122    
123            @Override
124            protected List<Fields> doGetFieldsListByClasses(
125                            long ddmStructureId, long[] classPKs, List<String> fieldNames,
126                            OrderByComparator orderByComparator)
127                    throws Exception {
128    
129                    return _doQuery(
130                            ddmStructureId, classPKs, fieldNames, null, orderByComparator);
131            }
132    
133            @Override
134            protected List<Fields> doGetFieldsListByDDMStructure(
135                            long ddmStructureId, List<String> fieldNames,
136                            OrderByComparator orderByComparator)
137                    throws Exception {
138    
139                    return _doQuery(ddmStructureId, fieldNames, null, orderByComparator);
140            }
141    
142            @Override
143            protected Map<Long, Fields> doGetFieldsMapByClasses(
144                            long ddmStructureId, long[] classPKs, List<String> fieldNames)
145                    throws Exception {
146    
147                    return _doQuery(ddmStructureId, classPKs, fieldNames);
148            }
149    
150            @Override
151            protected List<Fields> doQuery(
152                            long ddmStructureId, List<String> fieldNames, Condition condition,
153                            OrderByComparator orderByComparator)
154                    throws Exception {
155    
156                    return _doQuery(
157                            ddmStructureId, fieldNames, condition, orderByComparator);
158            }
159    
160            @Override
161            protected int doQueryCount(long ddmStructureId, Condition condition)
162                    throws Exception {
163    
164                    XPath conditionXPath = null;
165    
166                    if (condition != null) {
167                            conditionXPath = _parseCondition(condition);
168                    }
169    
170                    int count = 0;
171    
172                    long[] classPKs = _getStructureClassPKs(ddmStructureId);
173    
174                    for (long classPK : classPKs) {
175                            DDMContent ddmContent = DDMContentLocalServiceUtil.getContent(
176                                    classPK);
177    
178                            Document document = SAXReaderUtil.read(ddmContent.getXml());
179    
180                            if ((conditionXPath == null) ||
181                                    ((conditionXPath != null) &&
182                                      conditionXPath.booleanValueOf(document))) {
183    
184                                    count++;
185                            }
186                    }
187    
188                    return count;
189            }
190    
191            @Override
192            protected void doUpdate(
193                            long classPK, Fields fields, boolean mergeFields,
194                            ServiceContext serviceContext)
195                    throws Exception {
196    
197                    DDMContent ddmContent = DDMContentLocalServiceUtil.getContent(classPK);
198    
199                    Document document = null;
200    
201                    Element rootElement = null;
202    
203                    if (mergeFields) {
204                            document = SAXReaderUtil.read(ddmContent.getXml());
205    
206                            rootElement = document.getRootElement();
207                    }
208                    else {
209                            document = SAXReaderUtil.createDocument();
210    
211                            rootElement = document.addElement("root");
212                    }
213    
214                    Iterator<Field> itr = fields.iterator();
215    
216                    while (itr.hasNext()) {
217                            Field field = itr.next();
218    
219                            Object value = field.getValue();
220    
221                            if (value instanceof Date) {
222                                    Date valueDate = (Date)value;
223    
224                                    value = valueDate.getTime();
225                            }
226    
227                            String fieldName = field.getName();
228                            String fieldValue = String.valueOf(value);
229    
230                            Element dynamicElementElement = _getElementByName(
231                                    document, fieldName);
232    
233                            if (dynamicElementElement == null) {
234                                    _appendField(rootElement, fieldName, fieldValue);
235                            }
236                            else {
237                                    _updateField(dynamicElementElement, fieldName, fieldValue);
238                            }
239                    }
240    
241                    ddmContent.setModifiedDate(serviceContext.getModifiedDate(null));
242                    ddmContent.setXml(document.formattedString());
243    
244                    DDMContentLocalServiceUtil.updateContent(
245                            ddmContent.getPrimaryKey(), ddmContent.getName(),
246                            ddmContent.getDescription(), ddmContent.getXml(), serviceContext);
247            }
248    
249            private Element _appendField(
250                    Element rootElement, String fieldName, String fieldValue) {
251    
252                    Element dynamicElementElement = rootElement.addElement(
253                            "dynamic-element");
254    
255                    dynamicElementElement.addElement("dynamic-content");
256    
257                    _updateField(dynamicElementElement, fieldName, fieldValue);
258    
259                    return dynamicElementElement;
260            }
261    
262            private List<Fields> _doQuery(
263                            long ddmStructureId, List<String> fieldNames, Condition condition,
264                            OrderByComparator orderByComparator)
265                    throws Exception {
266    
267                    return _doQuery(
268                            ddmStructureId, _getStructureClassPKs(ddmStructureId), fieldNames,
269                            condition, orderByComparator);
270            }
271    
272            private Map<Long, Fields> _doQuery(
273                            long ddmStructureId, long[] classPKs, List<String> fieldNames)
274                    throws Exception {
275    
276                    Map<Long, Fields> fieldsMap = new HashMap<Long, Fields>();
277    
278                    List<Fields> fieldsList = _doQuery(
279                            ddmStructureId, classPKs, fieldNames, null, null);
280    
281                    for (int i = 0; i < fieldsList.size(); i++) {
282                            Fields fields = fieldsList.get(i);
283    
284                            fieldsMap.put(classPKs[i], fields);
285                    }
286    
287                    return fieldsMap;
288            }
289    
290            private List<Fields> _doQuery(
291                            long ddmStructureId, long[] classPKs, List<String> fieldNames,
292                            Condition condition, OrderByComparator orderByComparator)
293                    throws Exception {
294    
295                    List<Fields> fieldsList = new ArrayList<Fields>();
296    
297                    XPath conditionXPath = null;
298    
299                    if (condition != null) {
300                            conditionXPath = _parseCondition(condition);
301                    }
302    
303                    DDMStructure ddmStructure =
304                            DDMStructureLocalServiceUtil.getDDMStructure(ddmStructureId);
305    
306                    for (long classPK : classPKs) {
307                            DDMContent ddmContent = DDMContentLocalServiceUtil.getContent(
308                                    classPK);
309    
310                            Document document = SAXReaderUtil.read(ddmContent.getXml());
311    
312                            if ((conditionXPath != null) &&
313                                    !conditionXPath.booleanValueOf(document)) {
314    
315                                    continue;
316                            }
317    
318                            Fields fields = new Fields();
319    
320                            Element rootElement = document.getRootElement();
321    
322                            List<Element> dynamicElementElements = rootElement.elements(
323                                    "dynamic-element");
324    
325                            for (Element dynamicElementElement : dynamicElementElements) {
326                                    String fieldName = dynamicElementElement.attributeValue("name");
327                                    String fieldValue = dynamicElementElement.elementText(
328                                            "dynamic-content");
329    
330                                    if (!ddmStructure.hasField(fieldName) ||
331                                            ((fieldNames != null) && !fieldNames.contains(fieldName))) {
332    
333                                            continue;
334                                    }
335    
336                                    String fieldDataType = ddmStructure.getFieldDataType(fieldName);
337    
338                                    Serializable fieldValueSerializable =
339                                            FieldConstants.getSerializable(fieldDataType, fieldValue);
340    
341                                    Field field = new Field(
342                                            ddmStructureId, fieldName, fieldValueSerializable);
343    
344                                    fields.put(field);
345                            }
346    
347                            fieldsList.add(fields);
348                    }
349    
350                    if (orderByComparator != null) {
351                            Collections.sort(fieldsList, orderByComparator);
352                    }
353    
354                    return fieldsList;
355            }
356    
357            private Element _getElementByName(Document document, String name) {
358                    XPath xPathSelector = SAXReaderUtil.createXPath(
359                            "//dynamic-element[@name='".concat(name).concat("']"));
360    
361                    List<Node> nodes = xPathSelector.selectNodes(document);
362    
363                    if (nodes.size() == 1) {
364                            return (Element)nodes.get(0);
365                    }
366                    else {
367                            return null;
368                    }
369            }
370    
371            private long[] _getStructureClassPKs(long ddmStructureId)
372                    throws Exception {
373    
374                    List<Long> classPKs = new ArrayList<Long>();
375    
376                    List<DDMStorageLink> ddmStorageLinks =
377                            DDMStorageLinkLocalServiceUtil.getStructureStorageLinks(
378                                    ddmStructureId);
379    
380                    for (DDMStorageLink ddmStorageLink : ddmStorageLinks) {
381                            classPKs.add(ddmStorageLink.getClassPK());
382                    }
383    
384                    return ArrayUtil.toArray(classPKs.toArray(new Long[classPKs.size()]));
385            }
386    
387            private XPath _parseCondition(Condition condition) {
388                    StringBundler sb = new StringBundler(4);
389    
390                    sb.append("//dynamic-element");
391                    sb.append(StringPool.OPEN_BRACKET);
392                    sb.append(_toXPath(condition));
393                    sb.append(StringPool.CLOSE_BRACKET);
394    
395                    return SAXReaderUtil.createXPath(sb.toString());
396            }
397    
398            private String _toXPath(Condition condition) {
399                    StringBundler sb = new StringBundler();
400    
401                    if (condition.isJunction()) {
402                            sb.append(StringPool.OPEN_PARENTHESIS);
403                            sb.append(_toXPath((Junction)condition));
404                            sb.append(StringPool.CLOSE_PARENTHESIS);
405                    }
406                    else {
407                            sb.append(_toXPath((FieldConditionImpl)condition));
408                    }
409    
410                    return sb.toString();
411            }
412    
413            private String _toXPath(FieldCondition fieldCondition) {
414                    StringBundler sb = new StringBundler(6);
415    
416                    sb.append("(@name=");
417    
418                    String name = StringUtil.quote(
419                            String.valueOf(fieldCondition.getName()));
420    
421                    sb.append(name);
422    
423                    ComparisonOperator comparisonOperator =
424                            fieldCondition.getComparisonOperator();
425    
426                    if (comparisonOperator.equals(ComparisonOperator.LIKE)) {
427                            sb.append(" and matches(dynamic-content, ");
428                    }
429                    else {
430                            sb.append(" and dynamic-content= ");
431                    }
432    
433                    String value = StringUtil.quote(
434                            String.valueOf(fieldCondition.getValue()));
435    
436                    sb.append(value);
437    
438                    if (comparisonOperator.equals(ComparisonOperator.LIKE)) {
439                            sb.append(StringPool.CLOSE_PARENTHESIS);
440                    }
441    
442                    sb.append(StringPool.CLOSE_PARENTHESIS);
443    
444                    return sb.toString();
445            }
446    
447            private String _toXPath(Junction junction) {
448                    StringBundler sb = new StringBundler();
449    
450                    LogicalOperator logicalOperator = junction.getLogicalOperator();
451    
452                    String logicalOperatorString = logicalOperator.toString();
453    
454                    Iterator<Condition> itr = junction.iterator();
455    
456                    while (itr.hasNext()) {
457                            Condition condition = itr.next();
458    
459                            sb.append(_toXPath(condition));
460    
461                            if (itr.hasNext()) {
462                                    sb.append(StringPool.SPACE);
463                                    sb.append(logicalOperatorString.toLowerCase());
464                                    sb.append(StringPool.SPACE);
465                            }
466                    }
467    
468                    return sb.toString();
469            }
470    
471            private void _updateField(
472                    Element dynamicElementElement, String fieldName, String value) {
473    
474                    Element dynamicContentElement = dynamicElementElement.element(
475                            "dynamic-content");
476    
477                    dynamicElementElement.addAttribute("name", fieldName);
478    
479                    dynamicContentElement.clearContent();
480    
481                    dynamicContentElement.addCDATA(value);
482            }
483    
484    }