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