001    /**
002     * Copyright (c) 2000-2011 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
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                                    value = ((Date)value).getTime();
080                            }
081    
082                            _appendField(
083                                    rootElement, field.getName(), String.valueOf(value));
084                    }
085    
086                    DDMContent ddmContent = DDMContentLocalServiceUtil.addContent(
087                            serviceContext.getUserId(), serviceContext.getScopeGroupId(),
088                            DDMStorageLink.class.getName(), null, document.formattedString(),
089                            serviceContext);
090    
091                    DDMStorageLinkLocalServiceUtil.addStorageLink(
092                            classNameId, ddmContent.getPrimaryKey(), ddmStructureId,
093                            serviceContext);
094    
095                    return ddmContent.getPrimaryKey();
096            }
097    
098            @Override
099            protected void doDeleteByClass(long classPK) throws Exception {
100                    DDMContentLocalServiceUtil.deleteDDMContent(classPK);
101    
102                    DDMStorageLinkLocalServiceUtil.deleteClassStorageLink(classPK);
103            }
104    
105            @Override
106            protected void doDeleteByDDMStructure(long ddmStructureId)
107                    throws Exception {
108    
109                    List<DDMStorageLink> ddmStorageLinks =
110                            DDMStorageLinkLocalServiceUtil.getStructureStorageLinks(
111                                    ddmStructureId);
112    
113                    for (DDMStorageLink ddmStorageLink : ddmStorageLinks) {
114                            DDMContentLocalServiceUtil.deleteDDMContent(
115                                    ddmStorageLink.getClassPK());
116                    }
117    
118                    DDMStorageLinkLocalServiceUtil.deleteStructureStorageLinks(
119                            ddmStructureId);
120            }
121    
122            @Override
123            protected List<Fields> doGetFieldsListByClasses(
124                            long ddmStructureId, long[] classPKs, List<String> fieldNames,
125                            OrderByComparator orderByComparator)
126                    throws Exception {
127    
128                    return _doQuery(
129                            ddmStructureId, classPKs, fieldNames, null, orderByComparator);
130            }
131    
132            @Override
133            protected List<Fields> doGetFieldsListByDDMStructure(
134                            long ddmStructureId, List<String> fieldNames,
135                            OrderByComparator orderByComparator)
136                    throws Exception {
137    
138                    return _doQuery(ddmStructureId, fieldNames, null, orderByComparator);
139            }
140    
141            @Override
142            protected Map<Long, Fields> doGetFieldsMapByClasses(
143                            long ddmStructureId, long[] classPKs, List<String> fieldNames)
144                    throws Exception {
145    
146                    return _doQuery(ddmStructureId, classPKs, fieldNames);
147            }
148    
149            @Override
150            protected List<Fields> doQuery(
151                            long ddmStructureId, List<String> fieldNames, Condition condition,
152                            OrderByComparator orderByComparator)
153                    throws Exception {
154    
155                    return _doQuery(
156                            ddmStructureId, fieldNames, condition, orderByComparator);
157            }
158    
159            @Override
160            protected int doQueryCount(long ddmStructureId, Condition condition)
161                    throws Exception {
162    
163                    XPath conditionXPath = null;
164    
165                    if (condition != null) {
166                            conditionXPath = _parseCondition(condition);
167                    }
168    
169                    int count = 0;
170    
171                    long[] classPKs = _getStructureClassPKs(ddmStructureId);
172    
173                    for (long classPK : classPKs) {
174                            DDMContent ddmContent = DDMContentLocalServiceUtil.getContent(
175                                    classPK);
176    
177                            Document document = SAXReaderUtil.read(ddmContent.getXml());
178    
179                            if ((conditionXPath == null) ||
180                                    ((conditionXPath != null) &&
181                                      conditionXPath.booleanValueOf(document))) {
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                    DDMContent ddmContent = DDMContentLocalServiceUtil.getContent(classPK);
197    
198                    Document document = null;
199    
200                    Element rootElement = null;
201    
202                    if (mergeFields) {
203                            document = SAXReaderUtil.read(ddmContent.getXml());
204    
205                            rootElement = document.getRootElement();
206                    }
207                    else {
208                            document = SAXReaderUtil.createDocument();
209    
210                            rootElement = document.addElement("root");
211                    }
212    
213                    Iterator<Field> itr = fields.iterator();
214    
215                    while (itr.hasNext()) {
216                            Field field = itr.next();
217    
218                            String fieldName = field.getName();
219                            String fieldValue = String.valueOf(field.getValue());
220    
221                            Element dynamicElementElement = _getElementByName(
222                                    document, fieldName);
223    
224                            if (dynamicElementElement == null) {
225                                    _appendField(rootElement, fieldName, fieldValue);
226                            }
227                            else {
228                                    _updateField(dynamicElementElement, fieldName, fieldValue);
229                            }
230                    }
231    
232                    ddmContent.setModifiedDate(serviceContext.getModifiedDate(null));
233                    ddmContent.setXml(document.formattedString());
234    
235                    DDMContentLocalServiceUtil.updateContent(
236                            ddmContent.getPrimaryKey(), ddmContent.getName(),
237                            ddmContent.getDescription(), ddmContent.getXml(), serviceContext);
238            }
239    
240            private Element _appendField(
241                    Element rootElement, String fieldName, String fieldValue) {
242    
243                    Element dynamicElementElement = rootElement.addElement(
244                            "dynamic-element");
245    
246                    dynamicElementElement.addElement("dynamic-content");
247    
248                    _updateField(dynamicElementElement, fieldName, fieldValue);
249    
250                    return dynamicElementElement;
251            }
252    
253            private List<Fields> _doQuery(
254                            long ddmStructureId, List<String> fieldNames, Condition condition,
255                            OrderByComparator orderByComparator)
256                    throws Exception {
257    
258                    return _doQuery(
259                            ddmStructureId, _getStructureClassPKs(ddmStructureId), fieldNames,
260                            condition, orderByComparator);
261            }
262    
263            private Map<Long, Fields> _doQuery(
264                            long ddmStructureId, long[] classPKs, List<String> fieldNames)
265                    throws Exception {
266    
267                    Map<Long, Fields> fieldsMap = new HashMap<Long, Fields>();
268    
269                    List<Fields> fieldsList = _doQuery(
270                            ddmStructureId, classPKs, fieldNames, null, null);
271    
272                    for (int i = 0; i < fieldsList.size(); i++) {
273                            Fields fields = fieldsList.get(i);
274    
275                            fieldsMap.put(classPKs[i], fields);
276                    }
277    
278                    return fieldsMap;
279            }
280    
281            private List<Fields> _doQuery(
282                            long ddmStructureId, long[] classPKs, List<String> fieldNames,
283                            Condition condition, OrderByComparator orderByComparator)
284                    throws Exception {
285    
286                    List<Fields> fieldsList = new ArrayList<Fields>();
287    
288                    XPath conditionXPath = null;
289    
290                    if (condition != null) {
291                            conditionXPath = _parseCondition(condition);
292                    }
293    
294                    DDMStructure ddmStructure =
295                            DDMStructureLocalServiceUtil.getDDMStructure(ddmStructureId);
296    
297                    for (long classPK : classPKs) {
298                            DDMContent ddmContent = DDMContentLocalServiceUtil.getContent(
299                                    classPK);
300    
301                            Document document = SAXReaderUtil.read(ddmContent.getXml());
302    
303                            if ((conditionXPath != null) &&
304                                    !conditionXPath.booleanValueOf(document)) {
305    
306                                    continue;
307                            }
308    
309                            Fields fields = new Fields();
310    
311                            Element rootElement = document.getRootElement();
312    
313                            List<Element> dynamicElementElements = rootElement.elements(
314                                    "dynamic-element");
315    
316                            for (Element dynamicElementElement : dynamicElementElements) {
317                                    String fieldName = dynamicElementElement.attributeValue("name");
318                                    String fieldValue = dynamicElementElement.elementText(
319                                            "dynamic-content");
320    
321                                    if (!ddmStructure.hasField(fieldName) ||
322                                            ((fieldNames != null) && !fieldNames.contains(fieldName))) {
323    
324                                            continue;
325                                    }
326    
327                                    String fieldDataType = ddmStructure.getFieldDataType(fieldName);
328    
329                                    Serializable fieldValueSerializable =
330                                            FieldConstants.getSerializable(fieldDataType, fieldValue);
331    
332                                    Field field = new Field(
333                                            ddmStructureId, fieldName, fieldValueSerializable);
334    
335                                    fields.put(field);
336                            }
337    
338                            fieldsList.add(fields);
339                    }
340    
341                    if (orderByComparator != null) {
342                            Collections.sort(fieldsList, orderByComparator);
343                    }
344    
345                    return fieldsList;
346            }
347    
348            private Element _getElementByName(Document document, String name) {
349                    XPath xPathSelector = SAXReaderUtil.createXPath(
350                            "//dynamic-element[@name='".concat(name).concat("']"));
351    
352                    List<Node> nodes = xPathSelector.selectNodes(document);
353    
354                    if (nodes.size() == 1) {
355                            return (Element)nodes.get(0);
356                    }
357                    else {
358                            return null;
359                    }
360            }
361    
362            private long[] _getStructureClassPKs(long ddmStructureId)
363                    throws Exception {
364    
365                    List<Long> classPKs = new ArrayList<Long>();
366    
367                    List<DDMStorageLink> ddmStorageLinks =
368                            DDMStorageLinkLocalServiceUtil.getStructureStorageLinks(
369                                    ddmStructureId);
370    
371                    for (DDMStorageLink ddmStorageLink : ddmStorageLinks) {
372                            classPKs.add(ddmStorageLink.getClassPK());
373                    }
374    
375                    return ArrayUtil.toArray(classPKs.toArray(new Long[classPKs.size()]));
376            }
377    
378            private XPath _parseCondition(Condition condition) {
379                    StringBundler sb = new StringBundler(4);
380    
381                    sb.append("//dynamic-element");
382                    sb.append(StringPool.OPEN_BRACKET);
383                    sb.append(_toXPath(condition));
384                    sb.append(StringPool.CLOSE_BRACKET);
385    
386                    return SAXReaderUtil.createXPath(sb.toString());
387            }
388    
389            private String _toXPath(Condition condition) {
390                    StringBundler sb = new StringBundler();
391    
392                    if (condition.isJunction()) {
393                            sb.append(StringPool.OPEN_PARENTHESIS);
394                            sb.append(_toXPath((Junction)condition));
395                            sb.append(StringPool.CLOSE_PARENTHESIS);
396                    }
397                    else {
398                            sb.append(_toXPath((FieldConditionImpl)condition));
399                    }
400    
401                    return sb.toString();
402            }
403    
404            private String _toXPath(FieldCondition fieldCondition) {
405                    StringBundler sb = new StringBundler(6);
406    
407                    sb.append("(@name=");
408    
409                    String name = StringUtil.quote(
410                            String.valueOf(fieldCondition.getName()));
411    
412                    sb.append(name);
413    
414                    ComparisonOperator comparisonOperator =
415                            fieldCondition.getComparisonOperator();
416    
417                    if (comparisonOperator.equals(ComparisonOperator.LIKE)) {
418                            sb.append(" and matches(dynamic-content, ");
419                    }
420                    else {
421                            sb.append(" and dynamic-content= ");
422                    }
423    
424                    String value = StringUtil.quote(
425                            String.valueOf(fieldCondition.getValue()));
426    
427                    sb.append(value);
428    
429                    if (comparisonOperator.equals(ComparisonOperator.LIKE)) {
430                            sb.append(StringPool.CLOSE_PARENTHESIS);
431                    }
432    
433                    sb.append(StringPool.CLOSE_PARENTHESIS);
434    
435                    return sb.toString();
436            }
437    
438            private String _toXPath(Junction junction) {
439                    StringBundler sb = new StringBundler();
440    
441                    LogicalOperator logicalOperator = junction.getLogicalOperator();
442    
443                    String logicalOperatorString = logicalOperator.toString();
444    
445                    Iterator<Condition> itr = junction.iterator();
446    
447                    while (itr.hasNext()) {
448                            Condition condition = itr.next();
449    
450                            sb.append(_toXPath(condition));
451    
452                            if (itr.hasNext()) {
453                                    sb.append(StringPool.SPACE);
454                                    sb.append(logicalOperatorString.toLowerCase());
455                                    sb.append(StringPool.SPACE);
456                            }
457                    }
458    
459                    return sb.toString();
460            }
461    
462            private void _updateField(
463                    Element dynamicElementElement, String fieldName, String value) {
464    
465                    Element dynamicContentElement = dynamicElementElement.element(
466                            "dynamic-content");
467    
468                    dynamicElementElement.addAttribute("name", fieldName);
469    
470                    dynamicContentElement.clearContent();
471    
472                    dynamicContentElement.addCDATA(value);
473            }
474    
475    }