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.service.impl;
016    
017    import com.liferay.portal.LocaleException;
018    import com.liferay.portal.kernel.dao.orm.QueryUtil;
019    import com.liferay.portal.kernel.exception.PortalException;
020    import com.liferay.portal.kernel.exception.SystemException;
021    import com.liferay.portal.kernel.language.LanguageUtil;
022    import com.liferay.portal.kernel.log.Log;
023    import com.liferay.portal.kernel.log.LogFactoryUtil;
024    import com.liferay.portal.kernel.util.ArrayUtil;
025    import com.liferay.portal.kernel.util.CharPool;
026    import com.liferay.portal.kernel.util.GetterUtil;
027    import com.liferay.portal.kernel.util.HtmlUtil;
028    import com.liferay.portal.kernel.util.LocaleUtil;
029    import com.liferay.portal.kernel.util.OrderByComparator;
030    import com.liferay.portal.kernel.util.StringPool;
031    import com.liferay.portal.kernel.util.Validator;
032    import com.liferay.portal.kernel.xml.Document;
033    import com.liferay.portal.kernel.xml.DocumentException;
034    import com.liferay.portal.kernel.xml.Element;
035    import com.liferay.portal.kernel.xml.Node;
036    import com.liferay.portal.kernel.xml.SAXReaderUtil;
037    import com.liferay.portal.kernel.xml.XPath;
038    import com.liferay.portal.model.Group;
039    import com.liferay.portal.model.ResourceConstants;
040    import com.liferay.portal.model.User;
041    import com.liferay.portal.security.auth.CompanyThreadLocal;
042    import com.liferay.portal.service.ServiceContext;
043    import com.liferay.portal.util.PortalUtil;
044    import com.liferay.portal.util.PropsValues;
045    import com.liferay.portlet.dynamicdatamapping.NoSuchStructureException;
046    import com.liferay.portlet.dynamicdatamapping.RequiredStructureException;
047    import com.liferay.portlet.dynamicdatamapping.StructureDuplicateElementException;
048    import com.liferay.portlet.dynamicdatamapping.StructureDuplicateStructureKeyException;
049    import com.liferay.portlet.dynamicdatamapping.StructureNameException;
050    import com.liferay.portlet.dynamicdatamapping.StructureXsdException;
051    import com.liferay.portlet.dynamicdatamapping.model.DDMStructure;
052    import com.liferay.portlet.dynamicdatamapping.model.DDMStructureConstants;
053    import com.liferay.portlet.dynamicdatamapping.model.DDMTemplate;
054    import com.liferay.portlet.dynamicdatamapping.model.DDMTemplateConstants;
055    import com.liferay.portlet.dynamicdatamapping.service.base.DDMStructureLocalServiceBaseImpl;
056    import com.liferay.portlet.dynamicdatamapping.util.DDMTemplateHelperUtil;
057    import com.liferay.portlet.dynamicdatamapping.util.DDMXMLUtil;
058    
059    import java.util.ArrayList;
060    import java.util.Date;
061    import java.util.HashSet;
062    import java.util.List;
063    import java.util.Locale;
064    import java.util.Map;
065    import java.util.Set;
066    
067    /**
068     * The Dynamic Data Mapping (DDM) Structure local service is responsible for
069     * accessing, creating, modifying, and deleting structures.
070     *
071     * <p>
072     * DDM structures (structures) are used in Liferay to store structured content
073     * like document types, dynamic data definitions, or web contents.
074     * </p>
075     *
076     * <p>
077     * Structures support inheritance via parent structures. They also support
078     * multi-language names and descriptions.
079     * </p>
080     *
081     * <p>
082     * Structures can be related to many models in Liferay, such as those for web
083     * contents, dynamic data lists, and documents. This relationship can be
084     * established via the model's class name ID.
085     * </p>
086     *
087     * @author Brian Wing Shun Chan
088     * @author Bruno Basto
089     * @author Marcellus Tavares
090     * @author Juan Fernández
091     */
092    public class DDMStructureLocalServiceImpl
093            extends DDMStructureLocalServiceBaseImpl {
094    
095            /**
096             * Adds a structure referencing its parent structure.
097             *
098             * @param  userId the primary key of the structure's creator/owner
099             * @param  groupId the primary key of the group
100             * @param  parentStructureId the primary key of the parent structure
101             *         (optionally {@link
102             *         com.liferay.portlet.dynamicdatamapping.model.DDMStructureConstants#DEFAULT_PARENT_STRUCTURE_ID})
103             * @param  classNameId the primary key of the class name for the structure's
104             *         related model
105             * @param  structureKey the unique string identifying the structure
106             *         (optionally <code>null</code>)
107             * @param  nameMap the structure's locales and localized names
108             * @param  descriptionMap the structure's locales and localized descriptions
109             * @param  xsd the structure's XML schema definition
110             * @param  storageType the structure's storage type. It can be "xml" or
111             *         "expando". For more information, see {@link
112             *         com.liferay.portlet.dynamicdatamapping.storage.StorageType}.
113             * @param  type the structure's type. For more information, see {@link
114             *         com.liferay.portlet.dynamicdatamapping.model.DDMStructureConstants}.
115             * @param  serviceContext the structure's service context. Can set the UUID,
116             *         creation date, modification date, guest permissions, and group
117             *         permissions for the structure.
118             * @return the structure
119             * @throws PortalException if a user with the primary key could not be
120             *         found, if the XSD was not well-formed, or if a portal exception
121             *         occurred
122             * @throws SystemException if a system exception occurred
123             */
124            public DDMStructure addStructure(
125                            long userId, long groupId, long parentStructureId, long classNameId,
126                            String structureKey, Map<Locale, String> nameMap,
127                            Map<Locale, String> descriptionMap, String xsd, String storageType,
128                            int type, ServiceContext serviceContext)
129                    throws PortalException, SystemException {
130    
131                    // Structure
132    
133                    User user = userPersistence.findByPrimaryKey(userId);
134    
135                    if (Validator.isNull(structureKey)) {
136                            structureKey = String.valueOf(counterLocalService.increment());
137                    }
138                    else {
139                            structureKey = structureKey.trim().toUpperCase();
140                    }
141    
142                    try {
143                            xsd = DDMXMLUtil.formatXML(xsd);
144                    }
145                    catch (Exception e) {
146                            throw new StructureXsdException();
147                    }
148    
149                    Date now = new Date();
150    
151                    validate(groupId, classNameId, structureKey, nameMap, xsd);
152    
153                    long structureId = counterLocalService.increment();
154    
155                    DDMStructure structure = ddmStructurePersistence.create(structureId);
156    
157                    structure.setUuid(serviceContext.getUuid());
158                    structure.setGroupId(groupId);
159                    structure.setCompanyId(user.getCompanyId());
160                    structure.setUserId(user.getUserId());
161                    structure.setUserName(user.getFullName());
162                    structure.setCreateDate(serviceContext.getCreateDate(now));
163                    structure.setModifiedDate(serviceContext.getModifiedDate(now));
164                    structure.setParentStructureId(parentStructureId);
165                    structure.setClassNameId(classNameId);
166                    structure.setStructureKey(structureKey);
167                    structure.setNameMap(nameMap);
168                    structure.setDescriptionMap(descriptionMap);
169                    structure.setXsd(xsd);
170                    structure.setStorageType(storageType);
171                    structure.setType(type);
172    
173                    ddmStructurePersistence.update(structure);
174    
175                    // Resources
176    
177                    if (serviceContext.isAddGroupPermissions() ||
178                            serviceContext.isAddGuestPermissions()) {
179    
180                            addStructureResources(
181                                    structure, serviceContext.isAddGroupPermissions(),
182                                    serviceContext.isAddGuestPermissions());
183                    }
184                    else {
185                            addStructureResources(
186                                    structure, serviceContext.getGroupPermissions(),
187                                    serviceContext.getGuestPermissions());
188                    }
189    
190                    return structure;
191            }
192    
193            /**
194             * Adds a structure referencing a default parent structure, using the portal
195             * property <code>dynamic.data.lists.storage.type</code> storage type and
196             * default structure type.
197             *
198             * @param  userId the primary key of the structure's creator/owner
199             * @param  groupId the primary key of the group
200             * @param  classNameId the primary key of the class name for the structure's
201             *         related model
202             * @param  nameMap the structure's locales and localized names
203             * @param  descriptionMap the structure's locales and localized descriptions
204             * @param  xsd the structure's XML schema definition
205             * @param  serviceContext the structure's service context. Can set the UUID,
206             *         creation date, modification date, guest permissions, and group
207             *         permissions for the structure.
208             * @return the structure
209             * @throws PortalException if a user with the primary key could not be
210             *         found, if the XSD was not well-formed, or if a portal exception
211             *         occurred
212             * @throws SystemException if a system exception occurred
213             */
214            public DDMStructure addStructure(
215                            long userId, long groupId, long classNameId,
216                            Map<Locale, String> nameMap, Map<Locale, String> descriptionMap,
217                            String xsd, ServiceContext serviceContext)
218                    throws PortalException, SystemException {
219    
220                    return addStructure(
221                            userId, groupId, DDMStructureConstants.DEFAULT_PARENT_STRUCTURE_ID,
222                            classNameId, null, nameMap, descriptionMap, xsd,
223                            PropsValues.DYNAMIC_DATA_LISTS_STORAGE_TYPE,
224                            DDMStructureConstants.TYPE_DEFAULT, serviceContext);
225            }
226    
227            /**
228             * Adds a structure referencing a default parent structure if the parent
229             * structure is not found.
230             *
231             * @param  userId the primary key of the structure's creator/owner
232             * @param  groupId the primary key of the group
233             * @param  parentStructureKey the unique string identifying the parent
234             *         structure (optionally <code>null</code>)
235             * @param  classNameId the primary key of the class name for the structure's
236             *         related model
237             * @param  structureKey the unique string identifying the structure
238             *         (optionally <code>null</code>)
239             * @param  nameMap the structure's locales and localized names
240             * @param  descriptionMap the structure's locales and localized descriptions
241             * @param  xsd the structure's XML schema definition
242             * @param  storageType the structure's storage type. It can be "xml" or
243             *         "expando". For more information, see {@link
244             *         com.liferay.portlet.dynamicdatamapping.storage.StorageType}.
245             * @param  type the structure's type. For more information, see {@link
246             *         com.liferay.portlet.dynamicdatamapping.model.DDMStructureConstants}.
247             * @param  serviceContext the structure's service context. Can set the UUID,
248             *         creation date, modification date, guest permissions and group
249             *         permissions for the structure.
250             * @return the structure
251             * @throws PortalException if a user with the primary key could not be
252             *         found, if the XSD was not well-formed, or if a portal exception
253             *         occurred
254             * @throws SystemException if a system exception occurred
255             */
256            public DDMStructure addStructure(
257                            long userId, long groupId, String parentStructureKey,
258                            long classNameId, String structureKey, Map<Locale, String> nameMap,
259                            Map<Locale, String> descriptionMap, String xsd, String storageType,
260                            int type, ServiceContext serviceContext)
261                    throws PortalException, SystemException {
262    
263                    DDMStructure parentStructure = fetchStructure(
264                            groupId, classNameId, parentStructureKey);
265    
266                    long parentStructureId =
267                            DDMStructureConstants.DEFAULT_PARENT_STRUCTURE_ID;
268    
269                    if (parentStructure != null) {
270                            parentStructureId = parentStructure.getStructureId();
271                    }
272    
273                    return addStructure(
274                            userId, groupId, parentStructureId, classNameId, structureKey,
275                            nameMap, descriptionMap, xsd, storageType, type, serviceContext);
276            }
277    
278            /**
279             * Adds the resources to the structure.
280             *
281             * @param  structure the structure to add resources to
282             * @param  addGroupPermissions whether to add group permissions
283             * @param  addGuestPermissions whether to add guest permissions
284             * @throws PortalException if a portal exception occurred
285             * @throws SystemException if a system exception occurred
286             */
287            public void addStructureResources(
288                            DDMStructure structure, boolean addGroupPermissions,
289                            boolean addGuestPermissions)
290                    throws PortalException, SystemException {
291    
292                    resourceLocalService.addResources(
293                            structure.getCompanyId(), structure.getGroupId(),
294                            structure.getUserId(), DDMStructure.class.getName(),
295                            structure.getStructureId(), false, addGroupPermissions,
296                            addGuestPermissions);
297            }
298    
299            /**
300             * Adds the model resources with the permissions to the structure.
301             *
302             * @param  structure the structure to add resources to
303             * @param  groupPermissions the group permissions to be added
304             * @param  guestPermissions the guest permissions to be added
305             * @throws PortalException if a portal exception occurred
306             * @throws SystemException if a system exception occurred
307             */
308            public void addStructureResources(
309                            DDMStructure structure, String[] groupPermissions,
310                            String[] guestPermissions)
311                    throws PortalException, SystemException {
312    
313                    resourceLocalService.addModelResources(
314                            structure.getCompanyId(), structure.getGroupId(),
315                            structure.getUserId(), DDMStructure.class.getName(),
316                            structure.getStructureId(), groupPermissions, guestPermissions);
317            }
318    
319            /**
320             * Copies a structure, creating a new structure with all the values
321             * extracted from the original one. The new structure supports a new name
322             * and description.
323             *
324             * @param  userId the primary key of the structure's creator/owner
325             * @param  structureId the primary key of the structure to be copied
326             * @param  nameMap the new structure's locales and localized names
327             * @param  descriptionMap the new structure's locales and localized
328             *         descriptions
329             * @param  serviceContext the service context to be applied. Can set the
330             *         UUID, creation date, modification date, guest permissions, and
331             *         group permissions for the structure.
332             * @return the new structure
333             * @throws PortalException if a portal exception occurred
334             * @throws SystemException if a system exception occurred
335             */
336            public DDMStructure copyStructure(
337                            long userId, long structureId, Map<Locale, String> nameMap,
338                            Map<Locale, String> descriptionMap, ServiceContext serviceContext)
339                    throws PortalException, SystemException {
340    
341                    DDMStructure structure = ddmStructurePersistence.findByPrimaryKey(
342                            structureId);
343    
344                    return addStructure(
345                            userId, structure.getGroupId(), structure.getParentStructureId(),
346                            structure.getClassNameId(), null, nameMap, descriptionMap,
347                            structure.getXsd(), structure.getStorageType(), structure.getType(),
348                            serviceContext);
349            }
350    
351            public DDMStructure copyStructure(
352                            long userId, long structureId, ServiceContext serviceContext)
353                    throws PortalException, SystemException {
354    
355                    DDMStructure structure = ddmStructurePersistence.findByPrimaryKey(
356                            structureId);
357    
358                    return addStructure(
359                            userId, structure.getGroupId(), structure.getParentStructureId(),
360                            structure.getClassNameId(), null, structure.getNameMap(),
361                            structure.getDescriptionMap(), structure.getXsd(),
362                            structure.getStorageType(), structure.getType(), serviceContext);
363            }
364    
365            /**
366             * Deletes the structure and its resources.
367             *
368             * <p>
369             * Before deleting the structure, this method verifies whether the structure
370             * is required by another entity. If it is needed, an exception is thrown.
371             * </p>
372             *
373             * @param  structure the structure to be deleted
374             * @throws PortalException if a portal exception occurred
375             * @throws SystemException if a system exception occurred
376             */
377            public void deleteStructure(DDMStructure structure)
378                    throws PortalException, SystemException {
379    
380                    if (ddmStructureLinkPersistence.countByStructureId(
381                                    structure.getStructureId()) > 0) {
382    
383                            throw new RequiredStructureException(
384                                    RequiredStructureException.REFERENCED_STRUCTURE_LINK);
385                    }
386    
387                    long classNameId = PortalUtil.getClassNameId(DDMStructure.class);
388    
389                    if (ddmTemplatePersistence.countByG_C_C(
390                                    structure.getGroupId(), classNameId,
391                                    structure.getPrimaryKey()) > 0) {
392    
393                            throw new RequiredStructureException(
394                                    RequiredStructureException.REFERENCED_TEMPLATE);
395                    }
396    
397                    // Structure
398    
399                    ddmStructurePersistence.remove(structure);
400    
401                    // Resources
402    
403                    resourceLocalService.deleteResource(
404                            structure.getCompanyId(), DDMStructure.class.getName(),
405                            ResourceConstants.SCOPE_INDIVIDUAL, structure.getStructureId());
406            }
407    
408            /**
409             * Deletes the structure and its resources.
410             *
411             * <p>
412             * Before deleting the structure, the system verifies whether the structure
413             * is required by another entity. If it is needed, an exception is thrown.
414             * </p>
415             *
416             * @param  structureId the primary key of the structure to be deleted
417             * @throws PortalException if a portal exception occurred
418             * @throws SystemException if a system exception occurred
419             */
420            public void deleteStructure(long structureId)
421                    throws PortalException, SystemException {
422    
423                    DDMStructure structure = ddmStructurePersistence.findByPrimaryKey(
424                            structureId);
425    
426                    deleteStructure(structure);
427            }
428    
429            /**
430             * Deletes the matching structure and its resources.
431             *
432             * <p>
433             * Before deleting the structure, the system verifies whether the structure
434             * is required by another entity. If it is needed, an exception is thrown.
435             * </p>
436             *
437             * @param  groupId the primary key of the group
438             * @param  classNameId the primary key of the class name for the structure's
439             *         related model
440             * @param  structureKey the unique string identifying the structure
441             * @throws PortalException if a portal exception occurred
442             * @throws SystemException if a system exception occurred
443             */
444            public void deleteStructure(
445                            long groupId, long classNameId, String structureKey)
446                    throws PortalException, SystemException {
447    
448                    structureKey = structureKey.trim().toUpperCase();
449    
450                    DDMStructure structure = ddmStructurePersistence.findByG_C_S(
451                            groupId, classNameId, structureKey);
452    
453                    deleteStructure(structure);
454            }
455    
456            /**
457             * Deletes all the structures of the group.
458             *
459             * <p>
460             * Before deleting the structures, the system verifies whether each
461             * structure is required by another entity. If any of the structures are
462             * needed, an exception is thrown.
463             * </p>
464             *
465             * @param  groupId the primary key of the group
466             * @throws PortalException if a portal exception occurred
467             * @throws SystemException if a system exception occurred
468             */
469            public void deleteStructures(long groupId)
470                    throws PortalException, SystemException {
471    
472                    List<DDMStructure> structures = ddmStructurePersistence.findByGroupId(
473                            groupId);
474    
475                    for (DDMStructure structure : structures) {
476                            deleteStructure(structure);
477                    }
478            }
479    
480            /**
481             * Returns the structure with the ID.
482             *
483             * @param  structureId the primary key of the structure
484             * @return the structure with the structure ID, or <code>null</code> if a
485             *         matching structure could not be found
486             * @throws SystemException if a system exception occurred
487             */
488            public DDMStructure fetchStructure(long structureId)
489                    throws SystemException {
490    
491                    return ddmStructurePersistence.fetchByPrimaryKey(structureId);
492            }
493    
494            /**
495             * Returns the structure matching the class name ID, structure key, and
496             * group.
497             *
498             * @param  groupId the primary key of the group
499             * @param  classNameId the primary key of the class name for the structure's
500             *         related model
501             * @param  structureKey the unique string identifying the structure
502             * @return the matching structure, or <code>null</code> if a matching
503             *         structure could not be found
504             * @throws SystemException if a system exception occurred
505             */
506            public DDMStructure fetchStructure(
507                            long groupId, long classNameId, String structureKey)
508                    throws SystemException {
509    
510                    structureKey = structureKey.trim().toUpperCase();
511    
512                    return ddmStructurePersistence.fetchByG_C_S(
513                            groupId, classNameId, structureKey);
514            }
515    
516            /**
517             * Returns the structure matching the class name ID, structure key, and
518             * group, optionally in the global scope.
519             *
520             * <p>
521             * This method first searches in the group. If the structure is still not
522             * found and <code>includeGlobalStructures</code> is set to
523             * <code>true</code>, this method searches the global group.
524             * </p>
525             *
526             * @param  groupId the primary key of the group
527             * @param  classNameId the primary key of the class name for the structure's
528             *         related model
529             * @param  structureKey the unique string identifying the structure
530             * @param  includeGlobalStructures whether to include the global scope in
531             *         the search
532             * @return the matching structure, or <code>null</code> if a matching
533             *         structure could not be found
534             * @throws PortalException if a portal exception occurred
535             * @throws SystemException if a system exception occurred
536             */
537            public DDMStructure fetchStructure(
538                            long groupId, long classNameId, String structureKey,
539                            boolean includeGlobalStructures)
540                    throws PortalException, SystemException {
541    
542                    structureKey = structureKey.trim().toUpperCase();
543    
544                    DDMStructure structure = ddmStructurePersistence.fetchByG_C_S(
545                            groupId, classNameId, structureKey);
546    
547                    if ((structure != null) || !includeGlobalStructures) {
548                            return structure;
549                    }
550    
551                    Group group = groupPersistence.findByPrimaryKey(groupId);
552    
553                    Group companyGroup = groupLocalService.getCompanyGroup(
554                            group.getCompanyId());
555    
556                    return ddmStructurePersistence.fetchByG_C_S(
557                            companyGroup.getGroupId(), classNameId, structureKey);
558            }
559    
560            /**
561             * Returns the structure matching the UUID and group.
562             *
563             * @param  uuid the structure's UUID
564             * @param  groupId the primary key of the structure's group
565             * @return the matching structure, or <code>null</code> if a matching
566             *         structure could not be found
567             * @throws SystemException if a system exception occurred
568             */
569            public DDMStructure fetchStructure(String uuid, long groupId)
570                    throws SystemException {
571    
572                    return ddmStructurePersistence.fetchByUUID_G(uuid, groupId);
573            }
574    
575            /**
576             * @deprecated {@link #getClassStructures(long, long)}
577             */
578            public List<DDMStructure> getClassStructures(long classNameId)
579                    throws SystemException {
580    
581                    return ddmStructurePersistence.findByClassNameId(classNameId);
582            }
583    
584            /**
585             * @deprecated {@link #getClassStructures(long, long, int, int)}
586             */
587            public List<DDMStructure> getClassStructures(
588                            long classNameId, int start, int end)
589                    throws SystemException {
590    
591                    return ddmStructurePersistence.findByClassNameId(
592                            classNameId, start, end);
593            }
594    
595            /**
596             * Returns all the structures matching the class name ID.
597             *
598             * @param  companyId the primary key of the structure's company
599             * @param  classNameId the primary key of the class name for the structure's
600             *         related model
601             * @return the structures matching the class name ID
602             * @throws SystemException if a system exception occurred
603             */
604            public List<DDMStructure> getClassStructures(
605                            long companyId, long classNameId)
606                    throws SystemException {
607    
608                    return ddmStructurePersistence.findByC_C(companyId, classNameId);
609            }
610    
611            /**
612             * Returns a range of all the structures matching the class name ID.
613             *
614             * <p>
615             * Useful when paginating results. Returns a maximum of <code>end -
616             * start</code> instances. <code>start</code> and <code>end</code> are not
617             * primary keys, they are indexes in the result set. Thus, <code>0</code>
618             * refers to the first result in the set. Setting both <code>start</code>
619             * and <code>end</code> to {@link
620             * com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS} will return the full
621             * result set.
622             * </p>
623             *
624             * @param  companyId the primary key of the structure's company
625             * @param  classNameId the primary key of the class name for the structure's
626             *         related model
627             * @param  start the lower bound of the range of structures to return
628             * @param  end the upper bound of the range of structures to return (not
629             *         inclusive)
630             * @return the range of matching structures
631             * @throws SystemException if a system exception occurred
632             */
633            public List<DDMStructure> getClassStructures(
634                            long companyId, long classNameId, int start, int end)
635                    throws SystemException {
636    
637                    return ddmStructurePersistence.findByC_C(
638                            companyId, classNameId, start, end);
639            }
640    
641            /**
642             * Returns all the structures matching the class name ID ordered by the
643             * comparator.
644             *
645             * @param  companyId the primary key of the structure's company
646             * @param  classNameId the primary key of the class name for the structure's
647             *         related model
648             * @param  orderByComparator the comparator to order the structures
649             *         (optionally <code>null</code>)
650             * @return the matching structures ordered by the comparator
651             * @throws SystemException if a system exception occurred
652             */
653            public List<DDMStructure> getClassStructures(
654                            long companyId, long classNameId,
655                            OrderByComparator orderByComparator)
656                    throws SystemException {
657    
658                    return ddmStructurePersistence.findByC_C(
659                            companyId, classNameId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,
660                            orderByComparator);
661            }
662    
663            /**
664             * @deprecated {@link #getClassStructures(long, long, OrderByComparator)}
665             */
666            public List<DDMStructure> getClassStructures(
667                            long classNameId, OrderByComparator orderByComparator)
668                    throws SystemException {
669    
670                    return ddmStructurePersistence.findByClassNameId(
671                            classNameId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,
672                            orderByComparator);
673            }
674    
675            /**
676             * Returns all the structures for the document library file entry type.
677             *
678             * @param  dlFileEntryTypeId the primary key of the document library file
679             *         entry type
680             * @return the structures for the document library file entry type
681             * @throws SystemException if a system exception occurred
682             */
683            public List<DDMStructure> getDLFileEntryTypeStructures(
684                            long dlFileEntryTypeId)
685                    throws SystemException {
686    
687                    return dlFileEntryTypePersistence.getDDMStructures(dlFileEntryTypeId);
688            }
689    
690            /**
691             * Returns the structure with the ID.
692             *
693             * @param  structureId the primary key of the structure
694             * @return the structure with the ID
695             * @throws PortalException if a structure with the ID could not be found
696             * @throws SystemException if a system exception occurred
697             */
698            public DDMStructure getStructure(long structureId)
699                    throws PortalException, SystemException {
700    
701                    return ddmStructurePersistence.findByPrimaryKey(structureId);
702            }
703    
704            /**
705             * Returns the structure matching the class name ID, structure key, and
706             * group.
707             *
708             * @param  groupId the primary key of the structure's group
709             * @param  classNameId the primary key of the class name for the structure's
710             *         related model
711             * @param  structureKey the unique string identifying the structure
712             * @return the matching structure
713             * @throws PortalException if a matching structure could not be found
714             * @throws SystemException if a system exception occurred
715             */
716            public DDMStructure getStructure(
717                            long groupId, long classNameId, String structureKey)
718                    throws PortalException, SystemException {
719    
720                    structureKey = structureKey.trim().toUpperCase();
721    
722                    return ddmStructurePersistence.findByG_C_S(
723                            groupId, classNameId, structureKey);
724            }
725    
726            /**
727             * Returns the structure matching the class name ID, structure key, and
728             * group, optionally in the global scope.
729             *
730             * <p>
731             * This method first searches in the group. If the structure is still not
732             * found and <code>includeGlobalStructures</code> is set to
733             * <code>true</code>, this method searches the global group.
734             * </p>
735             *
736             * @param  groupId the primary key of the structure's group
737             * @param  classNameId the primary key of the class name for the structure's
738             *         related model
739             * @param  structureKey the unique string identifying the structure
740             * @param  includeGlobalStructures whether to include the global scope in
741             *         the search
742             * @return the matching structure
743             * @throws PortalException if a matching structure could not be found
744             * @throws SystemException if a system exception occurred
745             */
746            public DDMStructure getStructure(
747                            long groupId, long classNameId, String structureKey,
748                            boolean includeGlobalStructures)
749                    throws PortalException, SystemException {
750    
751                    structureKey = structureKey.trim().toUpperCase();
752    
753                    DDMStructure structure = ddmStructurePersistence.fetchByG_C_S(
754                            groupId, classNameId, structureKey);
755    
756                    if (structure != null) {
757                            return structure;
758                    }
759    
760                    if (!includeGlobalStructures) {
761                            throw new NoSuchStructureException(
762                                    "No DDMStructure exists with the structure key " +
763                                            structureKey);
764                    }
765    
766                    Group group = groupPersistence.findByPrimaryKey(groupId);
767    
768                    Group companyGroup = groupLocalService.getCompanyGroup(
769                            group.getCompanyId());
770    
771                    return ddmStructurePersistence.findByG_C_S(
772                            companyGroup.getGroupId(), classNameId, structureKey);
773            }
774    
775            /**
776             * Returns all the structures matching the group, name, and description.
777             *
778             * @param  groupId the primary key of the structure's group
779             * @param  name the structure's name
780             * @param  description the structure's description
781             * @return the matching structures
782             * @throws SystemException if a system exception occurred
783             */
784            public List<DDMStructure> getStructure(
785                            long groupId, String name, String description)
786                    throws SystemException {
787    
788                    return ddmStructurePersistence.findByG_N_D(groupId, name, description);
789            }
790    
791            /**
792             * @deprecated {@link #getStructures}
793             */
794            public List<DDMStructure> getStructureEntries() throws SystemException {
795                    return getStructures();
796            }
797    
798            /**
799             * @deprecated {@link #getStructures(long)}
800             */
801            public List<DDMStructure> getStructureEntries(long groupId)
802                    throws SystemException {
803    
804                    return getStructures(groupId);
805            }
806    
807            /**
808             * @deprecated {@link #getStructures(long, int, int)}
809             */
810            public List<DDMStructure> getStructureEntries(
811                            long groupId, int start, int end)
812                    throws SystemException {
813    
814                    return getStructures(groupId, start, end);
815            }
816    
817            /**
818             * Returns all the structures present in the system.
819             *
820             * @return the structures present in the system
821             * @throws SystemException if a system exception occurred
822             */
823            public List<DDMStructure> getStructures() throws SystemException {
824                    return ddmStructurePersistence.findAll();
825            }
826    
827            /**
828             * Returns all the structures present in the group.
829             *
830             * @param  groupId the primary key of the group
831             * @return the structures present in the group
832             * @throws SystemException if a system exception occurred
833             */
834            public List<DDMStructure> getStructures(long groupId)
835                    throws SystemException {
836    
837                    return ddmStructurePersistence.findByGroupId(groupId);
838            }
839    
840            /**
841             * Returns a range of all the structures belonging to the group.
842             *
843             * <p>
844             * Useful when paginating results. Returns a maximum of <code>end -
845             * start</code> instances. <code>start</code> and <code>end</code> are not
846             * primary keys, they are indexes in the result set. Thus, <code>0</code>
847             * refers to the first result in the set. Setting both <code>start</code>
848             * and <code>end</code> to {@link
849             * com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS} will return the full
850             * result set.
851             * </p>
852             *
853             * @param  groupId the primary key of the group
854             * @param  start the lower bound of the range of structures to return
855             * @param  end the upper bound of the range of structures to return (not
856             *         inclusive)
857             * @return the range of matching structures, or <code>null</code> if no
858             *         matches could be found
859             * @throws SystemException if a system exception occurred
860             */
861            public List<DDMStructure> getStructures(long groupId, int start, int end)
862                    throws SystemException {
863    
864                    return ddmStructurePersistence.findByGroupId(groupId, start, end);
865            }
866    
867            /**
868             * Returns all the structures matching class name ID and group.
869             *
870             * @param  groupId the primary key of the group
871             * @param  classNameId the primary key of the class name for the structure's
872             *         related model
873             * @return the matching structures
874             * @throws SystemException if a system exception occurred
875             */
876            public List<DDMStructure> getStructures(long groupId, long classNameId)
877                    throws SystemException {
878    
879                    return ddmStructurePersistence.findByG_C(groupId, classNameId);
880            }
881    
882            /**
883             * Returns a range of all the structures that match the class name ID and
884             * group.
885             *
886             * <p>
887             * Useful when paginating results. Returns a maximum of <code>end -
888             * start</code> instances. <code>start</code> and <code>end</code> are not
889             * primary keys, they are indexes in the result set. Thus, <code>0</code>
890             * refers to the first result in the set. Setting both <code>start</code>
891             * and <code>end</code> to {@link
892             * com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS} will return the full
893             * result set.
894             * </p>
895             *
896             * @param  groupId the primary key of the group
897             * @param  classNameId the primary key of the class name for the structure's
898             *         related model
899             * @param  start the lower bound of the range of structures to return
900             * @param  end the upper bound of the range of structures to return (not
901             *         inclusive)
902             * @return the matching structures, or <code>null</code> if no matching
903             *         structures could be found
904             * @throws SystemException if a system exception occurred
905             */
906            public List<DDMStructure> getStructures(
907                            long groupId, long classNameId, int start, int end)
908                    throws SystemException {
909    
910                    return ddmStructurePersistence.findByG_C(
911                            groupId, classNameId, start, end);
912            }
913    
914            /**
915             * Returns an ordered range of all the structures matching the class name ID
916             * and group.
917             *
918             * <p>
919             * Useful when paginating results. Returns a maximum of <code>end -
920             * start</code> instances. <code>start</code> and <code>end</code> are not
921             * primary keys, they are indexes in the result set. Thus, <code>0</code>
922             * refers to the first result in the set. Setting both <code>start</code>
923             * and <code>end</code> to {@link
924             * com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS} will return the full
925             * result set.
926             * </p>
927             *
928             * @param  groupId the primary key of the group
929             * @param  classNameId the primary key of the class name for the structure's
930             *         related model
931             * @param  start the lower bound of the range of structures to return
932             * @param  end the upper bound of the range of structures to return (not
933             *         inclusive)
934             * @param  orderByComparator the comparator to order the structures
935             *         (optionally <code>null</code>)
936             * @return the range of matching structures ordered by the comparator
937             * @throws SystemException if a system exception occurred
938             */
939            public List<DDMStructure> getStructures(
940                            long groupId, long classNameId, int start, int end,
941                            OrderByComparator orderByComparator)
942                    throws SystemException {
943    
944                    return ddmStructurePersistence.findByG_C(
945                            groupId, classNameId, start, end, orderByComparator);
946            }
947    
948            public List<DDMStructure> getStructures(
949                            long groupId, String name, String description)
950                    throws SystemException {
951    
952                    return ddmStructurePersistence.findByG_N_D(groupId, name, description);
953            }
954    
955            /**
956             * Returns all the structures belonging to the groups.
957             *
958             * @param  groupIds the primary keys of the groups
959             * @return the structures belonging to the groups
960             * @throws SystemException if a system exception occurred
961             */
962            public List<DDMStructure> getStructures(long[] groupIds)
963                    throws SystemException {
964    
965                    return ddmStructurePersistence.findByGroupId(groupIds);
966            }
967    
968            /**
969             * Returns the number of structures belonging to the group.
970             *
971             * @param  groupId the primary key of the group
972             * @return the number of structures belonging to the group
973             * @throws SystemException if a system exception occurred
974             */
975            public int getStructuresCount(long groupId) throws SystemException {
976                    return ddmStructurePersistence.countByGroupId(groupId);
977            }
978    
979            /**
980             * Returns the number of structures matching the class name ID and group.
981             *
982             * @param  groupId the primary key of the group
983             * @param  classNameId the primary key of the class name for the structure's
984             *         related model
985             * @return the number of matching structures
986             * @throws SystemException if a system exception occurred
987             */
988            public int getStructuresCount(long groupId, long classNameId)
989                    throws SystemException {
990    
991                    return ddmStructurePersistence.countByG_C(groupId, classNameId);
992            }
993    
994            /**
995             * Returns an ordered range of all the structures matching the groups and
996             * class name IDs, and matching the keywords in the structure names and
997             * descriptions.
998             *
999             * <p>
1000             * Useful when paginating results. Returns a maximum of <code>end -
1001             * start</code> instances. <code>start</code> and <code>end</code> are not
1002             * primary keys, they are indexes in the result set. Thus, <code>0</code>
1003             * refers to the first result in the set. Setting both <code>start</code>
1004             * and <code>end</code> to {@link
1005             * com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS} will return the full
1006             * result set.
1007             * </p>
1008             *
1009             * @param  companyId the primary key of the structure's company
1010             * @param  groupIds the primary keys of the groups
1011             * @param  classNameIds the primary keys of the class names of the models
1012             *         the structures are related to
1013             * @param  keywords the keywords (space separated), which may occur in the
1014             *         structure's name or description (optionally <code>null</code>)
1015             * @param  start the lower bound of the range of structures to return
1016             * @param  end the upper bound of the range of structures to return (not
1017             *         inclusive)
1018             * @param  orderByComparator the comparator to order the structures
1019             *         (optionally <code>null</code>)
1020             * @return the range of matching structures ordered by the comparator
1021             * @throws SystemException if a system exception occurred
1022             */
1023            public List<DDMStructure> search(
1024                            long companyId, long[] groupIds, long[] classNameIds,
1025                            String keywords, int start, int end,
1026                            OrderByComparator orderByComparator)
1027                    throws SystemException {
1028    
1029                    return ddmStructureFinder.findByKeywords(
1030                            companyId, groupIds, classNameIds, keywords, start, end,
1031                            orderByComparator);
1032            }
1033    
1034            /**
1035             * Returns an ordered range of all the structures matching the groups, class
1036             * name IDs, name keyword, description keyword, storage type, and type.
1037             *
1038             * <p>
1039             * Useful when paginating results. Returns a maximum of <code>end -
1040             * start</code> instances. <code>start</code> and <code>end</code> are not
1041             * primary keys, they are indexes in the result set. Thus, <code>0</code>
1042             * refers to the first result in the set. Setting both <code>start</code>
1043             * and <code>end</code> to {@link
1044             * com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS} will return the full
1045             * result set.
1046             * </p>
1047             *
1048             * @param  companyId the primary key of the structure's company
1049             * @param  groupIds the primary keys of the groups
1050             * @param  classNameIds the primary keys of the class names of the models
1051             *         the structures are related to
1052             * @param  name the name keywords
1053             * @param  description the description keywords
1054             * @param  storageType the structure's storage type. It can be "xml" or
1055             *         "expando". For more information, see {@link
1056             *         com.liferay.portlet.dynamicdatamapping.storage.StorageType}.
1057             * @param  type the structure's type. For more information, see {@link
1058             *         com.liferay.portlet.dynamicdatamapping.model.DDMStructureConstants}.
1059             * @param  andOperator whether every field must match its keywords, or just
1060             *         one field
1061             * @param  start the lower bound of the range of structures to return
1062             * @param  end the upper bound of the range of structures to return (not
1063             *         inclusive)
1064             * @param  orderByComparator the comparator to order the structures
1065             *         (optionally <code>null</code>)
1066             * @return the range of matching structures ordered by the comparator
1067             * @throws SystemException if a system exception occurred
1068             */
1069            public List<DDMStructure> search(
1070                            long companyId, long[] groupIds, long[] classNameIds, String name,
1071                            String description, String storageType, int type,
1072                            boolean andOperator, int start, int end,
1073                            OrderByComparator orderByComparator)
1074                    throws SystemException {
1075    
1076                    return ddmStructureFinder.findByC_G_C_N_D_S_T(
1077                            companyId, groupIds, classNameIds, name, description, storageType,
1078                            type, andOperator, start, end, orderByComparator);
1079            }
1080    
1081            /**
1082             * Returns the number of structures matching the groups and class name IDs,
1083             * and matching the keywords in the structure names and descriptions.
1084             *
1085             * @param  companyId the primary key of the structure's company
1086             * @param  groupIds the primary keys of the groups
1087             * @param  classNameIds the primary keys of the class names of the models
1088             *         the structures are related to
1089             * @param  keywords the keywords (space separated), which may occur in the
1090             *         structure's name or description (optionally <code>null</code>)
1091             * @return the number of matching structures
1092             * @throws SystemException if a system exception occurred
1093             */
1094            public int searchCount(
1095                            long companyId, long[] groupIds, long[] classNameIds,
1096                            String keywords)
1097                    throws SystemException {
1098    
1099                    return ddmStructureFinder.countByKeywords(
1100                            companyId, groupIds, classNameIds, keywords);
1101            }
1102    
1103            /**
1104             * Returns the number of structures matching the groups, class name IDs,
1105             * name keyword, description keyword, storage type, and type
1106             *
1107             * @param  companyId the primary key of the structure's company
1108             * @param  groupIds the primary keys of the groups
1109             * @param  classNameIds the primary keys of the class names of the models
1110             *         the structure's are related to
1111             * @param  name the name keywords
1112             * @param  description the description keywords
1113             * @param  storageType the structure's storage type. It can be "xml" or
1114             *         "expando". For more information, see {@link
1115             *         com.liferay.portlet.dynamicdatamapping.storage.StorageType}.
1116             * @param  type the structure's type. For more information, see {@link
1117             *         com.liferay.portlet.dynamicdatamapping.model.DDMStructureConstants}.
1118             * @param  andOperator whether every field must match its keywords, or just
1119             *         one field
1120             * @return the number of matching structures
1121             * @throws SystemException if a system exception occurred
1122             */
1123            public int searchCount(
1124                            long companyId, long[] groupIds, long[] classNameIds, String name,
1125                            String description, String storageType, int type,
1126                            boolean andOperator)
1127                    throws SystemException {
1128    
1129                    return ddmStructureFinder.countByC_G_C_N_D_S_T(
1130                            companyId, groupIds, classNameIds, name, description, storageType,
1131                            type, andOperator);
1132            }
1133    
1134            /**
1135             * Updates the structure matching the class name ID, structure key, and
1136             * group, replacing its old parent structure, name map, description map, and
1137             * XSD with new ones.
1138             *
1139             * @param  groupId the primary key of the group
1140             * @param  parentStructureId the primary key of the new parent structure
1141             * @param  classNameId the primary key of the class name for the structure's
1142             *         related model
1143             * @param  structureKey the unique string identifying the structure
1144             * @param  nameMap the structure's new locales and localized names
1145             * @param  descriptionMap the structure's new locales and localized
1146             *         description
1147             * @param  xsd the structure's new XML schema definition
1148             * @param  serviceContext the service context to be applied. Can set the
1149             *         structure's modification date.
1150             * @return the updated structure
1151             * @throws PortalException if a matching structure could not be found, if
1152             *         the XSD was not well-formed, or if a portal exception occurred
1153             * @throws SystemException if a system exception occurred
1154             */
1155            public DDMStructure updateStructure(
1156                            long groupId, long parentStructureId, long classNameId,
1157                            String structureKey, Map<Locale, String> nameMap,
1158                            Map<Locale, String> descriptionMap, String xsd,
1159                            ServiceContext serviceContext)
1160                    throws PortalException, SystemException {
1161    
1162                    structureKey = structureKey.trim().toUpperCase();
1163    
1164                    DDMStructure structure = ddmStructurePersistence.findByG_C_S(
1165                            groupId, classNameId, structureKey);
1166    
1167                    return doUpdateStructure(
1168                            parentStructureId, nameMap, descriptionMap, xsd, serviceContext,
1169                            structure);
1170            }
1171    
1172            /**
1173             * Updates the structure matching the structure ID, replacing its old parent
1174             * structure, name map, description map, and XSD with new ones.
1175             *
1176             * @param  structureId the primary key of the structure
1177             * @param  parentStructureId the primary key of the new parent structure
1178             * @param  nameMap the structure's new locales and localized names
1179             * @param  descriptionMap the structure's new locales and localized
1180             *         descriptions
1181             * @param  xsd the structure's new XML schema definition
1182             * @param  serviceContext the service context to be applied. Can set the
1183             *         structure's modification date.
1184             * @return the updated structure
1185             * @throws PortalException if a matching structure could not be found, if
1186             *         the XSD was not well-formed, or if a portal exception occurred
1187             * @throws SystemException if a system exception occurred
1188             */
1189            public DDMStructure updateStructure(
1190                            long structureId, long parentStructureId,
1191                            Map<Locale, String> nameMap, Map<Locale, String> descriptionMap,
1192                            String xsd, ServiceContext serviceContext)
1193                    throws PortalException, SystemException {
1194    
1195                    DDMStructure structure = ddmStructurePersistence.findByPrimaryKey(
1196                            structureId);
1197    
1198                    return doUpdateStructure(
1199                            parentStructureId, nameMap, descriptionMap, xsd, serviceContext,
1200                            structure);
1201            }
1202    
1203            /**
1204             * Updates the structure matching the structure ID, replacing its XSD with a
1205             * new one.
1206             *
1207             * @param  structureId the primary key of the structure
1208             * @param  xsd the structure's new XML schema definition
1209             * @param  serviceContext the service context to be applied. Can set the
1210             *         structure's modification date.
1211             * @return the updated structure
1212             * @throws PortalException if a matching structure could not be found, if
1213             *         the XSD was not well-formed, or if a portal exception occurred
1214             * @throws SystemException if a system exception occurred
1215             */
1216            public DDMStructure updateXSD(
1217                            long structureId, String xsd, ServiceContext serviceContext)
1218                    throws PortalException, SystemException {
1219    
1220                    DDMStructure structure = ddmStructurePersistence.findByPrimaryKey(
1221                            structureId);
1222    
1223                    return doUpdateStructure(
1224                            structure.getParentStructureId(), structure.getNameMap(),
1225                            structure.getDescriptionMap(), xsd, serviceContext, structure);
1226            }
1227    
1228            /**
1229             * Updates the structure matching the structure ID, replacing the metadata
1230             * entry of the named field.
1231             *
1232             * @param  structureId the primary key of the structure
1233             * @param  fieldName the name of the field whose metadata to update
1234             * @param  metadataEntryName the metadata entry's name
1235             * @param  metadataEntryValue the metadata entry's value
1236             * @param  serviceContext the service context to be applied. Can set the
1237             *         structure's modification date.
1238             * @throws PortalException if a matching structure could not be found, if
1239             *         the XSD was not well-formed, or if a portal exception occurred
1240             * @throws SystemException if a system exception occurred
1241             */
1242            public void updateXSDFieldMetadata(
1243                            long structureId, String fieldName, String metadataEntryName,
1244                            String metadataEntryValue, ServiceContext serviceContext)
1245                    throws PortalException, SystemException {
1246    
1247                    DDMStructure ddmStructure = fetchDDMStructure(structureId);
1248    
1249                    if (ddmStructure == null) {
1250                            return;
1251                    }
1252    
1253                    String xsd = ddmStructure.getXsd();
1254    
1255                    try {
1256                            Document document = SAXReaderUtil.read(xsd);
1257    
1258                            Element rootElement = document.getRootElement();
1259    
1260                            List<Element> dynamicElementElements = rootElement.elements(
1261                                    "dynamic-element");
1262    
1263                            for (Element dynamicElementElement : dynamicElementElements) {
1264                                    String dynamicElementElementFieldName = GetterUtil.getString(
1265                                            dynamicElementElement.attributeValue("name"));
1266    
1267                                    if (!dynamicElementElementFieldName.equals(fieldName)) {
1268                                            continue;
1269                                    }
1270    
1271                                    List<Element> metadataElements = dynamicElementElement.elements(
1272                                            "meta-data");
1273    
1274                                    for (Element metadataElement : metadataElements) {
1275                                            List<Element> metadataEntryElements =
1276                                                    metadataElement.elements();
1277    
1278                                            for (Element metadataEntryElement : metadataEntryElements) {
1279                                                    String metadataEntryElementName = GetterUtil.getString(
1280                                                            metadataEntryElement.attributeValue("name"));
1281    
1282                                                    if (metadataEntryElementName.equals(
1283                                                                    metadataEntryName)) {
1284    
1285                                                            metadataEntryElement.setText(metadataEntryValue);
1286                                                    }
1287                                            }
1288                                    }
1289                            }
1290    
1291                            updateXSD(structureId, document.asXML(), serviceContext);
1292                    }
1293                    catch (DocumentException de) {
1294                            throw new SystemException(de);
1295                    }
1296            }
1297    
1298            protected void appendNewStructureRequiredFields(
1299                    DDMStructure structure, Document templateDocument) {
1300    
1301                    String xsd = structure.getXsd();
1302    
1303                    Document structureDocument = null;
1304    
1305                    try {
1306                            structureDocument = SAXReaderUtil.read(xsd);
1307                    }
1308                    catch (DocumentException de) {
1309                            if (_log.isWarnEnabled()) {
1310                                    _log.warn(de, de);
1311                            }
1312    
1313                            return;
1314                    }
1315    
1316                    Element templateElement = templateDocument.getRootElement();
1317    
1318                    XPath structureXPath = SAXReaderUtil.createXPath(
1319                            "//dynamic-element[.//meta-data/entry[@name=\"required\"]=" +
1320                                    "\"true\"]");
1321    
1322                    List<Node> nodes = structureXPath.selectNodes(structureDocument);
1323    
1324                    for (Node node : nodes) {
1325                            Element element = (Element)node;
1326    
1327                            String name = element.attributeValue("name");
1328    
1329                            name = HtmlUtil.escapeXPathAttribute(name);
1330    
1331                            XPath templateXPath = SAXReaderUtil.createXPath(
1332                                    "//dynamic-element[@name=" + name + "]");
1333    
1334                            if (!templateXPath.booleanValueOf(templateDocument)) {
1335                                    templateElement.add(element.createCopy());
1336                            }
1337                    }
1338            }
1339    
1340            protected DDMStructure doUpdateStructure(
1341                            long parentStructureId, Map<Locale, String> nameMap,
1342                            Map<Locale, String> descriptionMap, String xsd,
1343                            ServiceContext serviceContext, DDMStructure structure)
1344                    throws PortalException, SystemException {
1345    
1346                    try {
1347                            xsd = DDMXMLUtil.formatXML(xsd);
1348                    }
1349                    catch (Exception e) {
1350                            throw new StructureXsdException();
1351                    }
1352    
1353                    validate(nameMap, xsd);
1354    
1355                    structure.setModifiedDate(serviceContext.getModifiedDate(null));
1356                    structure.setParentStructureId(parentStructureId);
1357                    structure.setNameMap(nameMap);
1358                    structure.setDescriptionMap(descriptionMap);
1359                    structure.setXsd(xsd);
1360    
1361                    ddmStructurePersistence.update(structure);
1362    
1363                    syncStructureTemplatesFields(structure);
1364    
1365                    return structure;
1366            }
1367    
1368            protected void syncStructureTemplatesFields(DDMStructure structure)
1369                    throws PortalException, SystemException {
1370    
1371                    long classNameId = PortalUtil.getClassNameId(DDMStructure.class);
1372    
1373                    List<DDMTemplate> templates = ddmTemplateLocalService.getTemplates(
1374                            structure.getGroupId(), classNameId, structure.getStructureId(),
1375                            DDMTemplateConstants.TEMPLATE_TYPE_FORM);
1376    
1377                    for (DDMTemplate template : templates) {
1378                            String script = template.getScript();
1379    
1380                            Document templateDocument = null;
1381    
1382                            try {
1383                                    templateDocument = SAXReaderUtil.read(script);
1384                            }
1385                            catch (DocumentException de) {
1386                                    if (_log.isWarnEnabled()) {
1387                                            _log.warn(de, de);
1388                                    }
1389    
1390                                    continue;
1391                            }
1392    
1393                            Element templateRootElement = templateDocument.getRootElement();
1394    
1395                            syncStructureTemplatesFields(template, templateRootElement);
1396    
1397                            appendNewStructureRequiredFields(structure, templateDocument);
1398    
1399                            try {
1400                                    script = DDMXMLUtil.formatXML(templateDocument.asXML());
1401                            }
1402                            catch (Exception e) {
1403                                    throw new StructureXsdException();
1404                            }
1405    
1406                            template.setScript(script);
1407    
1408                            ddmTemplatePersistence.update(template);
1409                    }
1410            }
1411    
1412            protected void syncStructureTemplatesFields(
1413                            DDMTemplate template, Element templateElement)
1414                    throws PortalException, SystemException {
1415    
1416                    DDMStructure structure = DDMTemplateHelperUtil.fetchStructure(template);
1417    
1418                    List<Element> dynamicElementElements = templateElement.elements(
1419                            "dynamic-element");
1420    
1421                    for (Element dynamicElementElement : dynamicElementElements) {
1422                            String dataType = dynamicElementElement.attributeValue("dataType");
1423                            String fieldName = dynamicElementElement.attributeValue("name");
1424    
1425                            if (Validator.isNull(dataType)) {
1426                                    continue;
1427                            }
1428    
1429                            if (!structure.hasField(fieldName)) {
1430                                    templateElement.remove(dynamicElementElement);
1431    
1432                                    continue;
1433                            }
1434    
1435                            String mode = template.getMode();
1436    
1437                            if (mode.equals(DDMTemplateConstants.TEMPLATE_MODE_CREATE)) {
1438                                    boolean fieldRequired = structure.getFieldRequired(fieldName);
1439    
1440                                    List<Element> metadataElements = dynamicElementElement.elements(
1441                                            "meta-data");
1442    
1443                                    for (Element metadataElement : metadataElements) {
1444                                            for (Element metadataEntryElement :
1445                                                            metadataElement.elements()) {
1446    
1447                                                    String attributeName =
1448                                                            metadataEntryElement.attributeValue("name");
1449    
1450                                                    if (fieldRequired && attributeName.equals("required")) {
1451                                                            metadataEntryElement.setText("true");
1452                                                    }
1453                                            }
1454                                    }
1455                            }
1456    
1457                            syncStructureTemplatesFields(template, dynamicElementElement);
1458                    }
1459            }
1460    
1461            protected void validate(List<Element> elements, Set<String> names)
1462                    throws PortalException {
1463    
1464                    for (Element element : elements) {
1465                            String elementName = element.getName();
1466    
1467                            if (elementName.equals("meta-data")) {
1468                                    continue;
1469                            }
1470    
1471                            String name = element.attributeValue("name", StringPool.BLANK);
1472                            String type = element.attributeValue("type", StringPool.BLANK);
1473    
1474                            if (Validator.isNull(name) ||
1475                                    name.startsWith(DDMStructureConstants.XSD_NAME_RESERVED)) {
1476    
1477                                    throw new StructureXsdException();
1478                            }
1479    
1480                            char[] charArray = name.toCharArray();
1481    
1482                            for (int i = 0; i < charArray.length; i++) {
1483                                    if (!Character.isLetterOrDigit(charArray[i]) &&
1484                                            (charArray[i] != CharPool.DASH) &&
1485                                            (charArray[i] != CharPool.UNDERLINE)) {
1486    
1487                                            throw new StructureXsdException();
1488                                    }
1489                            }
1490    
1491                            String path = name;
1492    
1493                            Element parentElement = element.getParent();
1494    
1495                            while (!parentElement.isRootElement()) {
1496                                    path =
1497                                            parentElement.attributeValue("name", StringPool.BLANK) +
1498                                                    StringPool.SLASH + path;
1499    
1500                                    parentElement = parentElement.getParent();
1501                            }
1502    
1503                            path = path.toLowerCase();
1504    
1505                            if (names.contains(path)) {
1506                                    throw new StructureDuplicateElementException();
1507                            }
1508                            else {
1509                                    names.add(path);
1510                            }
1511    
1512                            if (Validator.isNull(type)) {
1513                                    throw new StructureXsdException();
1514                            }
1515    
1516                            validate(element.elements(), names);
1517                    }
1518            }
1519    
1520            protected void validate(
1521                            long groupId, long classNameId, String structureKey,
1522                            Map<Locale, String> nameMap, String xsd)
1523                    throws PortalException, SystemException {
1524    
1525                    structureKey = structureKey.trim().toUpperCase();
1526    
1527                    DDMStructure structure = ddmStructurePersistence.fetchByG_C_S(
1528                            groupId, classNameId, structureKey);
1529    
1530                    if (structure != null) {
1531                            throw new StructureDuplicateStructureKeyException();
1532                    }
1533    
1534                    validate(nameMap, xsd);
1535            }
1536    
1537            protected void validate(Map<Locale, String> nameMap, String xsd)
1538                    throws PortalException {
1539    
1540                    if (Validator.isNull(xsd)) {
1541                            throw new StructureXsdException();
1542                    }
1543                    else {
1544                            try {
1545                                    List<Element> elements = new ArrayList<Element>();
1546    
1547                                    Document document = SAXReaderUtil.read(xsd);
1548    
1549                                    Element rootElement = document.getRootElement();
1550    
1551                                    List<Element> rootElementElements = rootElement.elements();
1552    
1553                                    if (rootElementElements.isEmpty()) {
1554                                            throw new StructureXsdException();
1555                                    }
1556    
1557                                    Locale contentDefaultLocale = LocaleUtil.fromLanguageId(
1558                                            rootElement.attributeValue("default-locale"));
1559    
1560                                    validateLanguages(nameMap, contentDefaultLocale);
1561    
1562                                    elements.addAll(rootElement.elements());
1563    
1564                                    Set<String> elNames = new HashSet<String>();
1565    
1566                                    validate(elements, elNames);
1567                            }
1568                            catch (LocaleException le) {
1569                                    throw le;
1570                            }
1571                            catch (StructureDuplicateElementException sdee) {
1572                                    throw sdee;
1573                            }
1574                            catch (StructureNameException sne) {
1575                                    throw sne;
1576                            }
1577                            catch (StructureXsdException sxe) {
1578                                    throw sxe;
1579                            }
1580                            catch (Exception e) {
1581                                    throw new StructureXsdException();
1582                            }
1583                    }
1584            }
1585    
1586            protected void validateLanguages(
1587                            Map<Locale, String> nameMap, Locale contentDefaultLocale)
1588                    throws PortalException {
1589    
1590                    String name = nameMap.get(contentDefaultLocale);
1591    
1592                    if (Validator.isNull(name)) {
1593                            throw new StructureNameException();
1594                    }
1595    
1596                    Locale[] availableLocales = LanguageUtil.getAvailableLocales();
1597    
1598                    if (!ArrayUtil.contains(availableLocales, contentDefaultLocale)) {
1599                            Long companyId = CompanyThreadLocal.getCompanyId();
1600    
1601                            LocaleException le = new LocaleException(
1602                                    "The locale " + contentDefaultLocale +
1603                                            " is not available in company " + companyId);
1604    
1605                            le.setSourceAvailableLocales(new Locale[] {contentDefaultLocale});
1606                            le.setTargetAvailableLocales(availableLocales);
1607    
1608                            throw le;
1609                    }
1610            }
1611    
1612            private static Log _log = LogFactoryUtil.getLog(
1613                    DDMStructureLocalServiceImpl.class);
1614    
1615    }