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 GmbH & Co. KG, 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.relations;
029
030import org.opencms.file.CmsObject;
031import org.opencms.file.CmsResource;
032import org.opencms.file.CmsResourceFilter;
033import org.opencms.main.CmsException;
034import org.opencms.main.CmsLog;
035import org.opencms.util.CmsUUID;
036
037import java.util.ArrayList;
038import java.util.Collections;
039import java.util.HashMap;
040import java.util.Iterator;
041import java.util.List;
042import java.util.Map;
043
044import org.apache.commons.logging.Log;
045
046/**
047 * Util class to find broken links in a bundle of resources.<p>
048 *
049 * @since 6.5.3
050 */
051public class CmsInternalLinksValidator {
052
053    /** The log object for this class. */
054    private static final Log LOG = CmsLog.getLog(CmsInternalLinksValidator.class);
055
056    /** The internal computed broken relations map. */
057    protected Map<String, List<CmsRelation>> m_brokenRelations;
058
059    /** The cms context object. */
060    private CmsObject m_cms;
061
062    /** The number of not visible resources with broken links. */
063    private int m_notVisibleResourcesCount;
064
065    /** All resources with broken links. */
066    private List<CmsResource> m_resourcesWithBrokenLinks;
067
068    /**
069     * Creates a new helper object.<p>
070     *
071     * @param cms the cms object
072     * @param resourceNames a list of resource names to be deleted
073     */
074    public CmsInternalLinksValidator(CmsObject cms, List<String> resourceNames) {
075
076        m_cms = cms;
077        m_brokenRelations = getBrokenRelations(resourceNames);
078    }
079
080    /**
081     * Returns all broken links for the given resource.<p>
082     *
083     * @param resourceName the resource to get the broken link
084     *
085     * @return a list of {@link CmsRelation} objects
086     */
087    public List<CmsRelation> getBrokenLinksForResource(String resourceName) {
088
089        return m_brokenRelations.get(resourceName);
090    }
091
092    /**
093     * Returns the number of not visible resources with broken links.<p>
094     *
095     * @return the number of not visible resources with broken links
096     */
097    public int getNotVisibleResourcesCount() {
098
099        if (m_resourcesWithBrokenLinks == null) {
100            // compute it if needed
101            getResourcesWithBrokenLinks();
102        }
103        return m_notVisibleResourcesCount;
104    }
105
106    /**
107     * Returns all resources with broken links.<p>
108     *
109     * @return a list of {@link org.opencms.file.CmsResource} objects
110     */
111    public List<CmsResource> getResourcesWithBrokenLinks() {
112
113        if (m_resourcesWithBrokenLinks == null) {
114            // sort the resulting hash map
115            List<String> resources = new ArrayList<String>(m_brokenRelations.keySet());
116            Collections.sort(resources);
117
118            m_resourcesWithBrokenLinks = new ArrayList<CmsResource>(resources.size());
119            m_notVisibleResourcesCount = 0;
120            // remove not visible resources
121            CmsResourceFilter filter = CmsResourceFilter.IGNORE_EXPIRATION.addRequireVisible();
122            String storedSiteRoot = m_cms.getRequestContext().getSiteRoot();
123            try {
124                m_cms.getRequestContext().setSiteRoot("/");
125                Iterator<String> itResources = resources.iterator();
126                while (itResources.hasNext()) {
127                    String resourceName = itResources.next();
128                    try {
129                        m_resourcesWithBrokenLinks.add(m_cms.readResource(resourceName, filter));
130                    } catch (Exception e) {
131                        // resource is not visible, increase count
132                        m_notVisibleResourcesCount++;
133                    }
134                }
135            } finally {
136                m_cms.getRequestContext().setSiteRoot(storedSiteRoot);
137            }
138        }
139        return m_resourcesWithBrokenLinks;
140    }
141
142    /**
143     * If no relation would be broken deleting the given resources.<p>
144     *
145     * @return <code>true</code> if no relation would be broken deleting the given resources
146     */
147    public boolean isEmpty() {
148
149        return m_brokenRelations.isEmpty();
150    }
151
152    /**
153     * Returns a map of where each entry has as key a name of a resource to be validated,
154     * and value a list of relations that are broken.<p>
155     *
156     * @param resourceNames a list of resource names to be validated
157     *
158     * @return a map of broken relations
159     */
160    private Map<String, List<CmsRelation>> getBrokenRelations(List<String> resourceNames) {
161
162        Map<String, List<CmsRelation>> brokenRelations = new HashMap<String, List<CmsRelation>>();
163
164        CmsRelationFilter filter = CmsRelationFilter.TARGETS.filterIncludeChildren().filterStructureId(
165            CmsUUID.getNullUUID());
166
167        Iterator<String> itFolders = resourceNames.iterator();
168        while (itFolders.hasNext()) {
169            String folderName = itFolders.next();
170            List<CmsRelation> relations;
171            try {
172                relations = m_cms.getRelationsForResource(folderName, filter);
173            } catch (CmsException e) {
174                LOG.error(Messages.get().getBundle().key(Messages.LOG_LINK_SEARCH_1, folderName), e);
175                continue;
176            }
177            Iterator<CmsRelation> itRelations = relations.iterator();
178            while (itRelations.hasNext()) {
179                CmsRelation relation = itRelations.next();
180                // target is broken
181                String resourceName = relation.getSourcePath();
182                List<CmsRelation> broken = brokenRelations.get(resourceName);
183                if (broken == null) {
184                    broken = new ArrayList<CmsRelation>();
185                    brokenRelations.put(resourceName, broken);
186                }
187                broken.add(relation);
188            }
189        }
190        return brokenRelations;
191    }
192}