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