001
014
015 package com.liferay.portlet.dynamicdatamapping.util;
016
017 import com.liferay.portal.kernel.exception.PortalException;
018 import com.liferay.portal.kernel.exception.SystemException;
019 import com.liferay.portal.kernel.log.Log;
020 import com.liferay.portal.kernel.log.LogFactoryUtil;
021 import com.liferay.portal.kernel.security.pacl.DoPrivileged;
022 import com.liferay.portal.kernel.util.HtmlUtil;
023 import com.liferay.portal.kernel.util.LocaleUtil;
024 import com.liferay.portal.kernel.util.StringBundler;
025 import com.liferay.portal.kernel.util.StringPool;
026 import com.liferay.portal.kernel.util.StringUtil;
027 import com.liferay.portal.kernel.xml.Attribute;
028 import com.liferay.portal.kernel.xml.Document;
029 import com.liferay.portal.kernel.xml.DocumentException;
030 import com.liferay.portal.kernel.xml.Element;
031 import com.liferay.portal.kernel.xml.Node;
032 import com.liferay.portal.kernel.xml.SAXReaderUtil;
033 import com.liferay.portal.kernel.xml.XMLSchema;
034 import com.liferay.portal.kernel.xml.XPath;
035 import com.liferay.portlet.dynamicdatamapping.StructureDefinitionException;
036 import com.liferay.portlet.dynamicdatamapping.StructureDuplicateElementException;
037 import com.liferay.portlet.dynamicdatamapping.model.DDMStructure;
038 import com.liferay.portlet.dynamicdatamapping.model.DDMStructureConstants;
039 import com.liferay.portlet.dynamicdatamapping.storage.Field;
040 import com.liferay.portlet.dynamicdatamapping.storage.FieldConstants;
041 import com.liferay.portlet.dynamicdatamapping.storage.Fields;
042 import com.liferay.util.xml.XMLFormatter;
043
044 import java.io.IOException;
045 import java.io.Serializable;
046
047 import java.util.Date;
048 import java.util.HashSet;
049 import java.util.Iterator;
050 import java.util.List;
051 import java.util.Locale;
052 import java.util.Set;
053
054
058 @DoPrivileged
059 public class DDMXMLImpl implements DDMXML {
060
061 @Override
062 public String formatXML(Document document) {
063 try {
064 return document.formattedString(_XML_INDENT);
065 }
066 catch (IOException ioe) {
067 throw new SystemException(ioe);
068 }
069 }
070
071 @Override
072 public String formatXML(String xml) {
073
074
075
076
077
078 try {
079 xml = StringUtil.replace(xml, "&#", "[$SPECIAL_CHARACTER$]");
080 xml = XMLFormatter.toString(xml, _XML_INDENT);
081 xml = StringUtil.replace(xml, "[$SPECIAL_CHARACTER$]", "&#");
082
083 return xml;
084 }
085 catch (IOException ioe) {
086 throw new SystemException(ioe);
087 }
088 catch (org.dom4j.DocumentException de) {
089 throw new SystemException(de);
090 }
091 }
092
093 @Override
094 public Fields getFields(DDMStructure structure, String xml)
095 throws PortalException {
096
097 return getFields(structure, null, xml, null);
098 }
099
100 @Override
101 public Fields getFields(
102 DDMStructure structure, XPath xPath, String xml,
103 List<String> fieldNames)
104 throws PortalException {
105
106 Document document = null;
107
108 try {
109 document = SAXReaderUtil.read(xml);
110 }
111 catch (DocumentException de) {
112 return null;
113 }
114
115 if ((xPath != null) && !xPath.booleanValueOf(document)) {
116 return null;
117 }
118
119 Fields fields = new Fields();
120
121 Element rootElement = document.getRootElement();
122
123 List<Element> dynamicElementElements = rootElement.elements(
124 "dynamic-element");
125
126 for (Element dynamicElementElement : dynamicElementElements) {
127 String fieldName = dynamicElementElement.attributeValue("name");
128
129 if (!structure.hasField(fieldName) ||
130 ((fieldNames != null) && !fieldNames.contains(fieldName))) {
131
132 continue;
133 }
134
135 String fieldDataType = structure.getFieldDataType(fieldName);
136
137 List<Element> dynamicContentElements =
138 dynamicElementElement.elements("dynamic-content");
139
140 for (Element dynamicContentElement : dynamicContentElements) {
141 String fieldValue = dynamicContentElement.getText();
142
143 String languageId = dynamicContentElement.attributeValue(
144 "language-id");
145
146 Locale locale = LocaleUtil.fromLanguageId(languageId);
147
148 Serializable fieldValueSerializable =
149 FieldConstants.getSerializable(fieldDataType, fieldValue);
150
151 Field field = fields.get(fieldName);
152
153 if (field == null) {
154 field = new Field();
155
156 String defaultLanguageId =
157 dynamicElementElement.attributeValue(
158 "default-language-id");
159
160 Locale defaultLocale = LocaleUtil.fromLanguageId(
161 defaultLanguageId);
162
163 field.setDefaultLocale(defaultLocale);
164
165 field.setDDMStructureId(structure.getStructureId());
166 field.setName(fieldName);
167 field.setValue(locale, fieldValueSerializable);
168
169 fields.put(field);
170 }
171 else {
172 field.addValue(locale, fieldValueSerializable);
173 }
174 }
175 }
176
177 return fields;
178 }
179
180 @Override
181 public String getXML(Document document, Fields fields) {
182 Element rootElement = null;
183
184 try {
185 if (document != null) {
186 rootElement = document.getRootElement();
187 }
188 else {
189 document = SAXReaderUtil.createDocument();
190
191 rootElement = document.addElement("root");
192 }
193
194 Iterator<Field> itr = fields.iterator(true);
195
196 while (itr.hasNext()) {
197 Field field = itr.next();
198
199 List<Node> nodes = getElementsByName(document, field.getName());
200
201 for (Node node : nodes) {
202 document.remove(node);
203 }
204
205 appendField(rootElement, field);
206 }
207
208 return document.formattedString();
209 }
210 catch (IOException ioe) {
211 throw new SystemException(ioe);
212 }
213 }
214
215 @Override
216 public String getXML(Fields fields) {
217 return getXML(null, fields);
218 }
219
220 public void setXMLSchema(XMLSchema xmlSchema) {
221 _xmlSchema = xmlSchema;
222 }
223
224 @Override
225 public String updateXMLDefaultLocale(
226 String xml, Locale contentDefaultLocale,
227 Locale contentNewDefaultLocale) {
228
229 try {
230 if (LocaleUtil.equals(
231 contentDefaultLocale, contentNewDefaultLocale)) {
232
233 return xml;
234 }
235
236 Document document = SAXReaderUtil.read(xml);
237
238 Element rootElement = document.getRootElement();
239
240 Attribute availableLocalesAttribute = rootElement.attribute(
241 _AVAILABLE_LOCALES);
242
243 String contentNewDefaultLanguageId = LocaleUtil.toLanguageId(
244 contentNewDefaultLocale);
245
246 String availableLocalesAttributeValue =
247 availableLocalesAttribute.getValue();
248
249 if (!availableLocalesAttributeValue.contains(
250 contentNewDefaultLanguageId)) {
251
252 StringBundler sb = new StringBundler(3);
253
254 sb.append(availableLocalesAttribute.getValue());
255 sb.append(StringPool.COMMA);
256 sb.append(contentNewDefaultLanguageId);
257
258 availableLocalesAttribute.setValue(sb.toString());
259 }
260
261 Attribute defaultLocaleAttribute = rootElement.attribute(
262 _DEFAULT_LOCALE);
263
264 defaultLocaleAttribute.setValue(contentNewDefaultLanguageId);
265
266 fixElementsDefaultLocale(
267 rootElement, contentDefaultLocale, contentNewDefaultLocale);
268
269 return document.formattedString();
270 }
271 catch (DocumentException de) {
272 throw new SystemException(de);
273 }
274 catch (IOException ioe) {
275 throw new SystemException(ioe);
276 }
277 }
278
279 @Override
280 public String validateXML(String xml) throws PortalException {
281 try {
282 Document document = SAXReaderUtil.read(xml, _xmlSchema);
283
284 validate(document);
285
286 return document.asXML();
287 }
288 catch (StructureDefinitionException sde) {
289 throw sde;
290 }
291 catch (StructureDuplicateElementException sdee) {
292 throw sdee;
293 }
294 catch (Exception e) {
295 if (_log.isDebugEnabled()) {
296 _log.debug("Invalid XML content " + e.getMessage(), e);
297 }
298
299 throw new StructureDefinitionException();
300 }
301 }
302
303 protected void appendField(Element element, Field field) {
304 Element dynamicElementElement = element.addElement("dynamic-element");
305
306 dynamicElementElement.addAttribute(
307 "default-language-id",
308 LocaleUtil.toLanguageId(field.getDefaultLocale()));
309 dynamicElementElement.addAttribute("name", field.getName());
310
311 for (Locale locale : field.getAvailableLocales()) {
312 List<Serializable> values = field.getValues(locale);
313
314 for (Serializable value : values) {
315 Element dynamicContentElement =
316 dynamicElementElement.addElement("dynamic-content");
317
318 dynamicContentElement.addAttribute(
319 "language-id", LocaleUtil.toLanguageId(locale));
320
321 updateField(dynamicContentElement, value);
322 }
323 }
324 }
325
326 protected void fixElementsDefaultLocale(
327 Element element, Locale contentDefaultLocale,
328 Locale contentNewDefaultLocale) {
329
330 for (Element dynamicElementElement :
331 element.elements(_DYNAMIC_ELEMENT)) {
332
333 Element importMetaDataElement =
334 (Element)dynamicElementElement.selectSingleNode(
335 "meta-data[@locale='" + contentNewDefaultLocale.toString() +
336 "']");
337
338 if (importMetaDataElement == null) {
339 Element metaDataElement =
340 (Element)dynamicElementElement.selectSingleNode(
341 "meta-data[@locale='" +
342 contentDefaultLocale.toString() + "']");
343
344 Element copiedMetadataElement = metaDataElement.createCopy();
345
346 Attribute localeAttribute = copiedMetadataElement.attribute(
347 _LOCALE);
348
349 String contentNewDefaultLanguageId = LocaleUtil.toLanguageId(
350 contentNewDefaultLocale);
351
352 localeAttribute.setValue(contentNewDefaultLanguageId);
353
354 dynamicElementElement.add(copiedMetadataElement);
355 }
356
357 fixElementsDefaultLocale(
358 dynamicElementElement, contentDefaultLocale,
359 contentNewDefaultLocale);
360 }
361 }
362
363 protected List<Node> getElementsByName(Document document, String name) {
364 name = HtmlUtil.escapeXPathAttribute(name);
365
366 XPath xPathSelector = SAXReaderUtil.createXPath(
367 "
368
369 return xPathSelector.selectNodes(document);
370 }
371
372 protected void updateField(
373 Element dynamicContentElement, Serializable fieldValue) {
374
375 dynamicContentElement.clearContent();
376
377 if (fieldValue instanceof Date) {
378 Date valueDate = (Date)fieldValue;
379
380 fieldValue = valueDate.getTime();
381 }
382
383 String valueString = String.valueOf(fieldValue);
384
385 dynamicContentElement.addCDATA(valueString.trim());
386 }
387
388 protected void validate(Document document) throws Exception {
389 XPath xPathSelector = SAXReaderUtil.createXPath("
390
391 List<Node> nodes = xPathSelector.selectNodes(document);
392
393 Set<String> elementNames = new HashSet<String>();
394
395 for (Node node : nodes) {
396 Element element = (Element)node;
397
398 String name = StringUtil.toLowerCase(
399 element.attributeValue("name"));
400
401 if (name.startsWith(DDMStructureConstants.XSD_NAME_RESERVED)) {
402 throw new StructureDefinitionException();
403 }
404
405 if (elementNames.contains(name)) {
406 throw new StructureDuplicateElementException();
407 }
408
409 elementNames.add(name);
410 }
411 }
412
413 private static final String _AVAILABLE_LOCALES = "available-locales";
414
415 private static final String _DEFAULT_LOCALE = "default-locale";
416
417 private static final String _DYNAMIC_ELEMENT = "dynamic-element";
418
419 private static final String _LOCALE = "locale";
420
421 private static final String _XML_INDENT = " ";
422
423 private static final Log _log = LogFactoryUtil.getLog(DDMXMLImpl.class);
424
425 private XMLSchema _xmlSchema;
426
427 }