001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.ade.publish.client;
029
030import org.opencms.ade.publish.client.CmsPublishItemStatus.Signal;
031import org.opencms.ade.publish.client.CmsPublishItemStatus.State;
032import org.opencms.ade.publish.shared.CmsPublishGroup;
033import org.opencms.ade.publish.shared.CmsPublishResource;
034import org.opencms.ade.publish.shared.CmsPublishResourceInfo.Type;
035import org.opencms.util.CmsUUID;
036
037import java.util.ArrayList;
038import java.util.HashSet;
039import java.util.List;
040import java.util.Map;
041import java.util.Set;
042
043import com.google.common.collect.ArrayListMultimap;
044import com.google.common.collect.Lists;
045import com.google.common.collect.Maps;
046import com.google.common.collect.Multimap;
047
048/**
049 * This class contains the data for the publish resources which are displayed
050 * in the publish dialog.<p>
051 *
052 * @since 8.0.0
053 */
054public class CmsPublishDataModel {
055
056    /**
057     * Predicate used to check if a resource has problems.<p>
058     */
059    public static class HasProblems implements I_CmsPublishResourceCheck {
060
061        /**
062         * @see org.opencms.ade.publish.client.CmsPublishDataModel.I_CmsPublishResourceCheck#check(org.opencms.ade.publish.shared.CmsPublishResource)
063         */
064        public boolean check(CmsPublishResource res) {
065
066            return hasProblems(res);
067        }
068    }
069
070    /**
071     * Predicate for testing properties of publish resources.<p>
072     */
073    public static interface I_CmsPublishResourceCheck {
074
075        /**
076         * Applies a boolean test to the publish resource and returns the result.<p>
077         *
078         * @param res the publish resource
079         * @return the result
080         */
081        boolean check(CmsPublishResource res);
082
083    }
084
085    /** The original publish groups. */
086    private List<CmsPublishGroup> m_groups;
087
088    /** The structure ids for each group. */
089    private List<List<CmsUUID>> m_idsByGroup = Lists.newArrayList();
090
091    /** The publish resources indexed by UUID. */
092    private Map<CmsUUID, CmsPublishResource> m_publishResources = Maps.newHashMap();
093
094    /** The publish resources indexed by path. */
095    private Map<String, CmsPublishResource> m_publishResourcesByPath = Maps.newHashMap();
096
097    /** Map from uuids of publish resources to uuids of their related resources. */
098    private Multimap<CmsUUID, CmsUUID> m_relatedIds = ArrayListMultimap.create();
099
100    /** Map containing the related publish resources. */
101    private Map<CmsUUID, CmsPublishResource> m_relatedPublishResources = Maps.newHashMap();
102
103    /** The action to execute when the selection changes. */
104    private Runnable m_selectionChangeAction;
105
106    /** The item status bean, indexed by structure id. */
107    private Map<CmsUUID, CmsPublishItemStatus> m_status = Maps.newHashMap();
108
109    /**
110     * Creates and initializes a new publish resource data model from a list of publish groups.<p>
111     *
112     * @param publishGroups the original publish groups
113     * @param handler the handler which should be notified of state changes
114     */
115    public CmsPublishDataModel(List<CmsPublishGroup> publishGroups, I_CmsPublishItemStatusUpdateHandler handler) {
116
117        m_groups = publishGroups;
118        for (CmsPublishGroup group : publishGroups) {
119            List<CmsUUID> idList = new ArrayList<CmsUUID>();
120            m_idsByGroup.add(idList);
121            for (CmsPublishResource res : group.getResources()) {
122                CmsPublishItemStatus status = new CmsPublishItemStatus(
123                    res.getId(),
124                    State.normal,
125                    hasProblems(res),
126                    handler);
127                m_status.put(res.getId(), status);
128                m_publishResources.put(res.getId(), res);
129                m_publishResourcesByPath.put(res.getName(), res);
130                idList.add(res.getId());
131                for (CmsPublishResource related : res.getRelated()) {
132                    m_relatedIds.put(res.getId(), related.getId());
133                    m_relatedPublishResources.put(related.getId(), related);
134                }
135
136            }
137        }
138    }
139
140    /**
141     * Returns if the given publish resource has problems preventing it from being published.<p>
142     *
143     * @param publishResource the publish resource
144     *
145     * @return <code>true</code> if the publish resource has problems
146     */
147    public static boolean hasProblems(CmsPublishResource publishResource) {
148
149        return (publishResource.getInfo() != null) && publishResource.getInfo().hasProblemType();
150    }
151
152    /**
153     * Collects group selection states.<p>
154     *
155     * @return the group selection states
156     */
157    public Map<Integer, CmsPublishItemStateSummary> computeGroupSelectionStates() {
158
159        Map<Integer, CmsPublishItemStateSummary> stateMap = Maps.newHashMap();
160
161        CmsPublishItemStateSummary allStates = new CmsPublishItemStateSummary();
162        int i = 0;
163        for (CmsPublishGroup group : m_groups) {
164            CmsPublishItemStateSummary groupStates = new CmsPublishItemStateSummary();
165            for (CmsPublishResource res : group.getResources()) {
166                CmsPublishItemStatus item = m_status.get(res.getId());
167                CmsPublishItemStatus.State stateToAdd;
168                if (item.isDisabled()) {
169                    // a disabled item should have no influence on the select/deselect all checkboxes,
170                    // just as an item which is marked to be removed
171                    stateToAdd = CmsPublishItemStatus.State.remove;
172                } else {
173                    stateToAdd = item.getState();
174                }
175                groupStates.addState(stateToAdd);
176                allStates.addState(stateToAdd);
177
178            }
179            stateMap.put(new Integer(i), groupStates);
180            i += 1;
181        }
182        stateMap.put(new Integer(-1), allStates);
183        return stateMap;
184    }
185
186    /**
187     * Counts the resources which have problems.<p>
188     *
189     * @return the number of resources which have problems
190     */
191    public int countProblems() {
192
193        return countResources(new HasProblems());
194    }
195
196    /**
197     * Counts the resources which pass a given check.<p>
198     *
199     * @param check the check to apply
200     *
201     * @return the number of resources which passed the check
202     */
203    public int countResources(I_CmsPublishResourceCheck check) {
204
205        int count = 0;
206        for (CmsPublishGroup group : m_groups) {
207            count += countResourcesInGroup(check, group.getResources());
208        }
209        return count;
210    }
211
212    /**
213     * Counts the resources of a group which pass a given check.<p>
214     *
215     * @param check the check to apply
216     * @param group the group of publish resources
217     *
218     * @return the number of resources in that group which passed the check
219     */
220    public int countResourcesInGroup(I_CmsPublishResourceCheck check, List<CmsPublishResource> group) {
221
222        int result = 0;
223        for (CmsPublishResource res : group) {
224            if (check.check(res)) {
225                result += 1;
226            }
227        }
228        return result;
229
230    }
231
232    /**
233     * Gets the list of publish groups.<p>
234     *
235     * @return the list of publish groups
236     */
237    public List<CmsPublishGroup> getGroups() {
238
239        return m_groups;
240    }
241
242    /**
243     * Gets the ids for a given publish group.<p>
244     *
245     * @param groupNum the index of the group
246     *
247     * @return the UUIDs for that group
248     */
249    public List<CmsUUID> getIdsForGroup(int groupNum) {
250
251        return m_idsByGroup.get(groupNum);
252    }
253
254    /**
255     * Returns the id's of all already published resources.<p>
256     *
257     * @return the id's of the already published resources
258     */
259    public List<CmsUUID> getIdsOfAlreadyPublishedResources() {
260
261        List<CmsUUID> alreadyPublished = new ArrayList<CmsUUID>();
262        List<CmsPublishResource> allResources = new ArrayList<CmsPublishResource>();
263        for (CmsPublishGroup group : m_groups) {
264
265            for (CmsPublishResource resource : group.getResources()) {
266                allResources.add(resource);
267                for (CmsPublishResource related : resource.getRelated()) {
268                    allResources.add(related);
269                }
270            }
271
272        }
273        for (CmsPublishResource resource : allResources) {
274            if ((resource.getInfo() != null) && (resource.getInfo().getType() == Type.PUBLISHED)) {
275                alreadyPublished.add(resource.getId());
276            }
277        }
278        return alreadyPublished;
279    }
280
281    /**
282     * Returns the ids of publish resources which should be published.<p>
283     *
284     * @return the ids of publish resources which should be published
285     */
286    public Set<CmsUUID> getPublishIds() {
287
288        Set<CmsUUID> toPublish = new HashSet<CmsUUID>();
289        for (Map.Entry<CmsUUID, CmsPublishItemStatus> entry : m_status.entrySet()) {
290            CmsUUID key = entry.getKey();
291            CmsPublishItemStatus status = entry.getValue();
292            if (status.getState() == State.publish) {
293                toPublish.add(key);
294                for (CmsUUID relatedId : m_relatedIds.get(key)) {
295                    CmsPublishResource relatedResource = m_relatedPublishResources.get(relatedId);
296                    if ((relatedResource != null) && !hasProblems(relatedResource)) {
297                        toPublish.add(relatedId);
298                    }
299                }
300            }
301        }
302        return toPublish;
303    }
304
305    /**
306     * Returns the list of all publish resources.<p>
307     *
308     * @return the list of all publish resources
309     */
310    public Map<CmsUUID, CmsPublishResource> getPublishResources() {
311
312        return m_publishResources;
313    }
314
315    /**
316     * Returns the map of publish resources by path.<p>
317     *
318     * @return the map of publish resources by path
319     */
320    public Map<String, CmsPublishResource> getPublishResourcesByPath() {
321
322        return m_publishResourcesByPath;
323    }
324
325    /**
326     * Returns the ids of publish resources which should be removed.<p>
327     *
328     * @return the ids of publish resources which should be removed
329     */
330    public List<CmsUUID> getRemoveIds() {
331
332        List<CmsUUID> toRemove = new ArrayList<CmsUUID>();
333        for (Map.Entry<CmsUUID, CmsPublishItemStatus> entry : m_status.entrySet()) {
334            CmsUUID key = entry.getKey();
335            CmsPublishItemStatus status = entry.getValue();
336            if (status.getState() == State.remove) {
337                toRemove.add(key);
338            }
339        }
340        return toRemove;
341    }
342
343    /**
344     * Returns the status for a given publish resource id.<p>
345     *
346     * @param id the publish resource's structure id
347     * @return the status for that publish resource
348     */
349    public CmsPublishItemStatus getStatus(CmsUUID id) {
350
351        return m_status.get(id);
352    }
353
354    /**
355     * Checks if there is only a single group of resources.<p>
356     *
357     * @return true if there is only a single group of resources
358     */
359    public boolean hasSingleGroup() {
360
361        return m_groups.size() == 1;
362    }
363
364    /**
365     * Checks if there are any publish resources.<p>
366     *
367     * @return true if there are no publish resources at all
368     */
369    public boolean isEmpty() {
370
371        return m_status.isEmpty();
372    }
373
374    /**
375     * Sets the action which should be executed when the selection changes.<p>
376     *
377     * @param action the action to run when the selection changes
378     */
379    public void setSelectionChangeAction(Runnable action) {
380
381        m_selectionChangeAction = action;
382    }
383
384    /**
385     * Sends a signal to a publish item status bean with the given id.<p>
386     *
387     * @param signal the signal
388     * @param id the structure id
389     */
390    public void signal(Signal signal, CmsUUID id) {
391
392        getStatus(id).handleSignal(signal);
393        runSelectionChangeAction();
394    }
395
396    /**
397     * Sends a signal to all publish item status beans.<p>
398     *
399     * @param signal the signal
400     */
401    public void signalAll(Signal signal) {
402
403        for (Map.Entry<CmsUUID, CmsPublishItemStatus> entry : m_status.entrySet()) {
404            entry.getValue().handleSignal(signal);
405        }
406        runSelectionChangeAction();
407    }
408
409    /**
410     * Sends a signal to all publish items in a given group.<p>
411     *
412     * @param signal the signal to send
413     * @param groupNum the group index
414     */
415    public void signalGroup(Signal signal, int groupNum) {
416
417        CmsPublishGroup group = m_groups.get(groupNum);
418        for (CmsPublishResource res : group.getResources()) {
419            CmsUUID id = res.getId();
420            m_status.get(id).handleSignal(signal);
421        }
422        runSelectionChangeAction();
423    }
424
425    /**
426     * Executes the action defined for selection changes.<p>
427     */
428    private void runSelectionChangeAction() {
429
430        if (m_selectionChangeAction != null) {
431            m_selectionChangeAction.run();
432        }
433    }
434
435}