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