001    /**
002     * Copyright (c) 2000-present 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.HtmlUtil;
019    import com.liferay.portal.kernel.util.OrderByComparator;
020    import com.liferay.portal.kernel.util.StringBundler;
021    import com.liferay.portal.kernel.util.StringPool;
022    import com.liferay.portal.kernel.util.StringUtil;
023    import com.liferay.portal.kernel.xml.Document;
024    import com.liferay.portal.kernel.xml.SAXReaderUtil;
025    import com.liferay.portal.kernel.xml.XPath;
026    import com.liferay.portal.service.ServiceContext;
027    import com.liferay.portal.util.PortalUtil;
028    import com.liferay.portlet.dynamicdatamapping.model.DDMContent;
029    import com.liferay.portlet.dynamicdatamapping.model.DDMStorageLink;
030    import com.liferay.portlet.dynamicdatamapping.model.DDMStructure;
031    import com.liferay.portlet.dynamicdatamapping.service.DDMContentLocalServiceUtil;
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.FieldConditionImpl;
038    import com.liferay.portlet.dynamicdatamapping.storage.query.Junction;
039    import com.liferay.portlet.dynamicdatamapping.storage.query.LogicalOperator;
040    import com.liferay.portlet.dynamicdatamapping.util.DDMUtil;
041    import com.liferay.portlet.dynamicdatamapping.util.DDMXMLUtil;
042    
043    import java.util.ArrayList;
044    import java.util.Collections;
045    import java.util.HashMap;
046    import java.util.Iterator;
047    import java.util.List;
048    import java.util.Map;
049    
050    /**
051     * @author Eduardo Lundgren
052     * @author Brian Wing Shun Chan
053     */
054    public class XMLStorageAdapter extends BaseStorageAdapter {
055    
056            @Override
057            public String getStorageType() {
058                    return StorageType.XML.toString();
059            }
060    
061            @Override
062            protected long doCreate(
063                            long companyId, long ddmStructureId, Fields fields,
064                            ServiceContext serviceContext)
065                    throws Exception {
066    
067                    long classNameId = PortalUtil.getClassNameId(
068                            DDMContent.class.getName());
069    
070                    DDMContent ddmContent = DDMContentLocalServiceUtil.addContent(
071                            serviceContext.getUserId(), serviceContext.getScopeGroupId(),
072                            DDMStorageLink.class.getName(), null, DDMXMLUtil.getXML(fields),
073                            serviceContext);
074    
075                    DDMStorageLinkLocalServiceUtil.addStorageLink(
076                            classNameId, ddmContent.getPrimaryKey(), ddmStructureId,
077                            serviceContext);
078    
079                    return ddmContent.getPrimaryKey();
080            }
081    
082            @Override
083            protected void doDeleteByClass(long classPK) throws Exception {
084                    DDMContentLocalServiceUtil.deleteDDMContent(classPK);
085    
086                    DDMStorageLinkLocalServiceUtil.deleteClassStorageLink(classPK);
087            }
088    
089            @Override
090            protected void doDeleteByDDMStructure(long ddmStructureId)
091                    throws Exception {
092    
093                    List<DDMStorageLink> ddmStorageLinks =
094                            DDMStorageLinkLocalServiceUtil.getStructureStorageLinks(
095                                    ddmStructureId);
096    
097                    for (DDMStorageLink ddmStorageLink : ddmStorageLinks) {
098                            DDMContentLocalServiceUtil.deleteDDMContent(
099                                    ddmStorageLink.getClassPK());
100                    }
101    
102                    DDMStorageLinkLocalServiceUtil.deleteStructureStorageLinks(
103                            ddmStructureId);
104            }
105    
106            @Override
107            protected List<Fields> doGetFieldsListByClasses(
108                            long ddmStructureId, long[] classPKs, List<String> fieldNames,
109                            OrderByComparator<Fields> orderByComparator)
110                    throws Exception {
111    
112                    return _doQuery(
113                            ddmStructureId, classPKs, fieldNames, null, orderByComparator);
114            }
115    
116            @Override
117            protected List<Fields> doGetFieldsListByDDMStructure(
118                            long ddmStructureId, List<String> fieldNames,
119                            OrderByComparator<Fields> orderByComparator)
120                    throws Exception {
121    
122                    return _doQuery(ddmStructureId, fieldNames, null, orderByComparator);
123            }
124    
125            @Override
126            protected Map<Long, Fields> doGetFieldsMapByClasses(
127                            long ddmStructureId, long[] classPKs, List<String> fieldNames)
128                    throws Exception {
129    
130                    return _doQuery(ddmStructureId, classPKs, fieldNames);
131            }
132    
133            @Override
134            protected List<Fields> doQuery(
135                            long ddmStructureId, List<String> fieldNames, Condition condition,
136                            OrderByComparator<Fields> orderByComparator)
137                    throws Exception {
138    
139                    return _doQuery(
140                            ddmStructureId, fieldNames, condition, orderByComparator);
141            }
142    
143            @Override
144            protected int doQueryCount(long ddmStructureId, Condition condition)
145                    throws Exception {
146    
147                    XPath conditionXPath = null;
148    
149                    if (condition != null) {
150                            conditionXPath = _parseCondition(condition);
151                    }
152    
153                    int count = 0;
154    
155                    long[] classPKs = _getStructureClassPKs(ddmStructureId);
156    
157                    for (long classPK : classPKs) {
158                            DDMContent ddmContent = DDMContentLocalServiceUtil.getContent(
159                                    classPK);
160    
161                            Document document = SAXReaderUtil.read(ddmContent.getData());
162    
163                            if ((conditionXPath == null) ||
164                                    ((conditionXPath != null) &&
165                                     conditionXPath.booleanValueOf(document))) {
166    
167                                    count++;
168                            }
169                    }
170    
171                    return count;
172            }
173    
174            @Override
175            protected void doUpdate(
176                            long classPK, Fields fields, boolean mergeFields,
177                            ServiceContext serviceContext)
178                    throws Exception {
179    
180                    DDMContent ddmContent = DDMContentLocalServiceUtil.getContent(classPK);
181    
182                    ddmContent.setModifiedDate(serviceContext.getModifiedDate(null));
183    
184                    if (mergeFields) {
185                            fields = DDMUtil.mergeFields(fields, getFields(classPK));
186                    }
187    
188                    ddmContent.setData(DDMXMLUtil.getXML(fields));
189    
190                    DDMContentLocalServiceUtil.updateContent(
191                            ddmContent.getPrimaryKey(), ddmContent.getName(),
192                            ddmContent.getDescription(), ddmContent.getData(), serviceContext);
193            }
194    
195            private List<Fields> _doQuery(
196                            long ddmStructureId, List<String> fieldNames, Condition condition,
197                            OrderByComparator<Fields> orderByComparator)
198                    throws Exception {
199    
200                    return _doQuery(
201                            ddmStructureId, _getStructureClassPKs(ddmStructureId), fieldNames,
202                            condition, orderByComparator);
203            }
204    
205            private Map<Long, Fields> _doQuery(
206                            long ddmStructureId, long[] classPKs, List<String> fieldNames)
207                    throws Exception {
208    
209                    Map<Long, Fields> fieldsMap = new HashMap<Long, Fields>();
210    
211                    List<Fields> fieldsList = _doQuery(
212                            ddmStructureId, classPKs, fieldNames, null, null);
213    
214                    for (int i = 0; i < fieldsList.size(); i++) {
215                            Fields fields = fieldsList.get(i);
216    
217                            fieldsMap.put(classPKs[i], fields);
218                    }
219    
220                    return fieldsMap;
221            }
222    
223            private List<Fields> _doQuery(
224                            long ddmStructureId, long[] classPKs, List<String> fieldNames,
225                            Condition condition, OrderByComparator<Fields> orderByComparator)
226                    throws Exception {
227    
228                    List<Fields> fieldsList = new ArrayList<Fields>();
229    
230                    XPath conditionXPath = null;
231    
232                    if (condition != null) {
233                            conditionXPath = _parseCondition(condition);
234                    }
235    
236                    DDMStructure ddmStructure =
237                            DDMStructureLocalServiceUtil.getDDMStructure(ddmStructureId);
238    
239                    for (long classPK : classPKs) {
240                            DDMContent ddmContent = DDMContentLocalServiceUtil.getContent(
241                                    classPK);
242    
243                            Fields fields = DDMXMLUtil.getFields(
244                                    ddmStructure, conditionXPath, ddmContent.getData(), fieldNames);
245    
246                            fieldsList.add(fields);
247                    }
248    
249                    if (orderByComparator != null) {
250                            Collections.sort(fieldsList, orderByComparator);
251                    }
252    
253                    return fieldsList;
254            }
255    
256            private long[] _getStructureClassPKs(long ddmStructureId) throws Exception {
257                    List<Long> classPKs = new ArrayList<Long>();
258    
259                    List<DDMStorageLink> ddmStorageLinks =
260                            DDMStorageLinkLocalServiceUtil.getStructureStorageLinks(
261                                    ddmStructureId);
262    
263                    for (DDMStorageLink ddmStorageLink : ddmStorageLinks) {
264                            classPKs.add(ddmStorageLink.getClassPK());
265                    }
266    
267                    return ArrayUtil.toArray(classPKs.toArray(new Long[classPKs.size()]));
268            }
269    
270            private XPath _parseCondition(Condition condition) {
271                    StringBundler sb = new StringBundler(4);
272    
273                    sb.append("//dynamic-element");
274                    sb.append(StringPool.OPEN_BRACKET);
275                    sb.append(_toXPath(condition));
276                    sb.append(StringPool.CLOSE_BRACKET);
277    
278                    return SAXReaderUtil.createXPath(sb.toString());
279            }
280    
281            private String _toXPath(Condition condition) {
282                    StringBundler sb = new StringBundler(3);
283    
284                    if (condition.isJunction()) {
285                            sb.append(StringPool.OPEN_PARENTHESIS);
286                            sb.append(_toXPath((Junction)condition));
287                            sb.append(StringPool.CLOSE_PARENTHESIS);
288                    }
289                    else {
290                            sb.append(_toXPath((FieldConditionImpl)condition));
291                    }
292    
293                    return sb.toString();
294            }
295    
296            private String _toXPath(FieldCondition fieldCondition) {
297                    StringBundler sb = new StringBundler(6);
298    
299                    sb.append("(@name=");
300    
301                    String name = HtmlUtil.escapeXPathAttribute(
302                            String.valueOf(fieldCondition.getName()));
303    
304                    sb.append(name);
305    
306                    ComparisonOperator comparisonOperator =
307                            fieldCondition.getComparisonOperator();
308    
309                    if (comparisonOperator.equals(ComparisonOperator.LIKE)) {
310                            sb.append(" and matches(dynamic-content, ");
311                    }
312                    else {
313                            sb.append(" and dynamic-content= ");
314                    }
315    
316                    String value = HtmlUtil.escapeXPathAttribute(
317                            String.valueOf(fieldCondition.getValue()));
318    
319                    sb.append(value);
320    
321                    if (comparisonOperator.equals(ComparisonOperator.LIKE)) {
322                            sb.append(StringPool.CLOSE_PARENTHESIS);
323                    }
324    
325                    sb.append(StringPool.CLOSE_PARENTHESIS);
326    
327                    return sb.toString();
328            }
329    
330            private String _toXPath(Junction junction) {
331                    StringBundler sb = new StringBundler();
332    
333                    LogicalOperator logicalOperator = junction.getLogicalOperator();
334    
335                    String logicalOperatorString = logicalOperator.toString();
336    
337                    Iterator<Condition> itr = junction.iterator();
338    
339                    while (itr.hasNext()) {
340                            Condition condition = itr.next();
341    
342                            sb.append(_toXPath(condition));
343    
344                            if (itr.hasNext()) {
345                                    sb.append(StringPool.SPACE);
346                                    sb.append(StringUtil.toLowerCase(logicalOperatorString));
347                                    sb.append(StringPool.SPACE);
348                            }
349                    }
350    
351                    return sb.toString();
352            }
353    
354    }