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.lock;
029
030import org.opencms.file.CmsUser;
031import org.opencms.main.CmsLog;
032import org.opencms.util.CmsUUID;
033
034import java.util.Collections;
035import java.util.HashSet;
036import java.util.Set;
037
038import org.apache.commons.logging.Log;
039
040/**
041 * A filter to retrieve the locks.<p>
042 *
043 * @since 6.5.4
044 */
045public final class CmsLockFilter implements Cloneable {
046
047    /** Logger instance for this class. */
048    private static final Log LOG = CmsLog.getLog(CmsLockFilter.class);
049
050    /** To filter all locks. */
051    public static final CmsLockFilter FILTER_ALL = new CmsLockFilter(true).filterIncludeChildren();
052
053    /** To filter all inherited locks. */
054    public static final CmsLockFilter FILTER_INHERITED = new CmsLockFilter(true);
055
056    /** To filter all non inherited locks. */
057    public static final CmsLockFilter FILTER_NON_INHERITED = new CmsLockFilter(false);
058
059    /** If set the filter restricts the result excluding locks owned by the given user. */
060    private CmsUUID m_notOwnedByUserId;
061
062    /** If set the filter extends the result to non inherited locks. */
063    private boolean m_includeChildren;
064
065    /** If set the filter restricts the result including only locks owned by the given user. */
066    private CmsUUID m_ownedByUserId;
067
068    /** If set the filter extends the result to inherited locks. */
069    private boolean m_includeParents;
070
071    /** If set the filter restricts the result to the given project. */
072    private CmsUUID m_projectId;
073
074    /** If set the filter also matches shared exclusive locks. */
075    private boolean m_sharedExclusive;
076
077    /** The types to filter. */
078    private Set<CmsLockType> m_types = new HashSet<CmsLockType>();
079
080    /** If set the filter restricts the result excluding locks not lockable by the given user. */
081    private CmsUser m_notLockableByUser;
082
083    /** If set the filter restricts the result including only locks lockable by the given user. */
084    private CmsUser m_lockableByUser;
085
086    /**
087     * Private constructor.<p>
088     *
089     * @param inherited if the this lock filter should checks inherited locks or not
090     */
091    private CmsLockFilter(boolean inherited) {
092
093        m_includeChildren = !inherited;
094        m_includeParents = inherited;
095    }
096
097    /**
098     * @see java.lang.Object#clone()
099     */
100    @Override
101    public Object clone() {
102
103        CmsLockFilter filter = new CmsLockFilter(false);
104        filter.m_includeChildren = m_includeChildren;
105        filter.m_includeParents = m_includeParents;
106        filter.m_types = new HashSet<CmsLockType>(m_types);
107        filter.m_ownedByUserId = m_ownedByUserId;
108        filter.m_notOwnedByUserId = m_notOwnedByUserId;
109        filter.m_projectId = m_projectId;
110        filter.m_notLockableByUser = m_notLockableByUser;
111        filter.m_lockableByUser = m_lockableByUser;
112        return filter;
113    }
114
115    /**
116     * Returns an extended filter that will extend the result to the given path and all its children.<p>
117     *
118     * @return an extended filter to search the subresources of the given path
119     */
120    public CmsLockFilter filterIncludeChildren() {
121
122        CmsLockFilter filter = (CmsLockFilter)clone();
123        filter.m_includeChildren = true;
124        return filter;
125    }
126
127    /**
128     * Returns an extended filter that will extend the result to the given path and all its parents.<p>
129     *
130     * @return an extended filter to search the subresources of the given path
131     */
132    public CmsLockFilter filterIncludeParents() {
133
134        CmsLockFilter filter = (CmsLockFilter)clone();
135        filter.m_includeParents = true;
136        return filter;
137    }
138
139    /**
140     * Returns an extended filter with the given user restriction.<p>
141     *
142     * @param user the user to filter
143     *
144     * @return an extended filter with the given user restriction
145     */
146    public CmsLockFilter filterLockableByUser(CmsUser user) {
147
148        CmsLockFilter filter = (CmsLockFilter)clone();
149        filter.m_lockableByUser = user;
150        return filter;
151    }
152
153    /**
154     * Returns an extended filter with the given user restriction.<p>
155     *
156     * @param user the user to filter
157     *
158     * @return an extended filter with the given user restriction
159     */
160    public CmsLockFilter filterNotLockableByUser(CmsUser user) {
161
162        CmsLockFilter filter = (CmsLockFilter)clone();
163        filter.m_notLockableByUser = user;
164        return filter;
165    }
166
167    /**
168     * Returns an extended filter with the given user restriction.<p>
169     *
170     * @param userId the user id to filter
171     *
172     * @return an extended filter with the given user restriction
173     */
174    public CmsLockFilter filterNotOwnedByUserId(CmsUUID userId) {
175
176        CmsLockFilter filter = (CmsLockFilter)clone();
177        filter.m_notOwnedByUserId = userId;
178        return filter;
179    }
180
181    /**
182     * Returns an extended filter with the given user restriction.<p>
183     *
184     * @param userId the user id to filter
185     *
186     * @return an extended filter with the given user restriction
187     */
188    public CmsLockFilter filterOwnedByUserId(CmsUUID userId) {
189
190        CmsLockFilter filter = (CmsLockFilter)clone();
191        filter.m_ownedByUserId = userId;
192        return filter;
193    }
194
195    /**
196     * Returns an extended filter with the given project restriction.<p>
197     *
198     * @param projectId the project to filter the locks with
199     *
200     * @return an extended filter with the given project restriction
201     */
202    public CmsLockFilter filterProject(CmsUUID projectId) {
203
204        CmsLockFilter filter = (CmsLockFilter)clone();
205        filter.m_projectId = projectId;
206        return filter;
207    }
208
209    /**
210     * Returns an extended filter that also matches shared exclusive locks (siblings).<p>
211     *
212     * @return an extended filter that also matches shared exclusive locks
213     */
214    public CmsLockFilter filterSharedExclusive() {
215
216        CmsLockFilter filter = (CmsLockFilter)clone();
217        filter.m_sharedExclusive = true;
218        return filter;
219    }
220
221    /**
222     * Returns an extended filter with the given type restriction.<p>
223     *
224     * @param type the lock type to filter
225     *
226     * @return an extended filter with the given type restriction
227     */
228    public CmsLockFilter filterType(CmsLockType type) {
229
230        CmsLockFilter filter = (CmsLockFilter)clone();
231        filter.m_types.add(type);
232        return filter;
233    }
234
235    /**
236     * Returns the user that can overwrite the locks.<p>
237     *
238     * @return the user that can overwrite the locks
239     */
240    public CmsUser getLockableByUserId() {
241
242        return m_lockableByUser;
243    }
244
245    /**
246     * Returns the user that can not overwrite the locks.<p>
247     *
248     * @return the user that can not overwrite the locks
249     */
250    public CmsUser getNotLockableByUserId() {
251
252        return m_notLockableByUser;
253    }
254
255    /**
256     * Returns the user that has not to own the locks.<p>
257     *
258     * @return the user that has not to own the locks
259     */
260    public CmsUUID getNotOwnedByUserId() {
261
262        return m_notOwnedByUserId;
263    }
264
265    /**
266     * Returns the user that has to own the locks.<p>
267     *
268     * @return the user that has to own the locks
269     */
270    public CmsUUID getOwnedByUserId() {
271
272        return m_ownedByUserId;
273    }
274
275    /**
276     * Returns the project restriction.<p>
277     *
278     * @return the project restriction
279     */
280    public CmsUUID getProjectId() {
281
282        return m_projectId;
283    }
284
285    /**
286     * Returns the types to filter.<p>
287     *
288     * @return the types to filter
289     */
290    public Set<CmsLockType> getTypes() {
291
292        return Collections.unmodifiableSet(m_types);
293    }
294
295    /**
296     * Returns the include children flag.<p>
297     *
298     * @return if set the filter extends the result to the given path and all its children
299     */
300    public boolean isIncludeChildren() {
301
302        return m_includeChildren;
303    }
304
305    /**
306     * Returns the include parents flag.<p>
307     *
308     * @return if set the filter extends the result to the given path and all its parents
309     */
310    public boolean isIncludeParent() {
311
312        return m_includeParents;
313    }
314
315    /**
316     * Returns the <code>true</code> if this filter also matches shared exclusive locks.<p>
317     *
318     * @return the <code>true</code> if this filter also matches shared exclusive locks
319     */
320    public boolean isSharedExclusive() {
321
322        return m_sharedExclusive;
323    }
324
325    /**
326     * Matches the given lock against this filter and the given path.<p>
327     *
328     * @param rootPath the path to match the lock against
329     * @param lock the lock to match
330     *
331     * @return <code>true</code> if the given lock matches
332     */
333    public boolean match(String rootPath, CmsLock lock) {
334
335        return match(rootPath, lock, false);
336    }
337
338    /**
339     * @see java.lang.Object#toString()
340     */
341    @Override
342    public String toString() {
343
344        StringBuffer str = new StringBuffer(128);
345        str.append("[");
346        str.append("children").append("=").append(m_includeChildren).append(", ");
347        str.append("parents").append("=").append(m_includeParents).append(", ");
348        str.append("types").append("=").append(m_types).append(", ");
349        str.append("includedUser").append("=").append(m_ownedByUserId).append(", ");
350        str.append("excludedUser").append("=").append(m_notOwnedByUserId).append(", ");
351        str.append("project").append("=").append(m_projectId).append(", ");
352        str.append("lockableBy").append("=").append(m_lockableByUser).append(", ");
353        str.append("notLockableBy").append("=").append(m_notLockableByUser).append(", ");
354        str.append("includeShared").append("=").append(m_sharedExclusive);
355        str.append("]");
356        return str.toString();
357    }
358
359    /**
360     * Matches the given lock against this filter and the given path.<p>
361     *
362     * @param rootPath the path to match the lock against
363     * @param lock the lock to match
364     * @param isRelatedCheck if we are currently in the recursive call for the related lock
365     *
366     * @return <code>true</code> if the given lock matches
367     */
368    protected boolean match(String rootPath, CmsLock lock, boolean isRelatedCheck) {
369
370        boolean match = false;
371        if (m_includeChildren) {
372            // safe since rootPath always ends with slash if a folder
373            match = lock.getResourceName().startsWith(rootPath);
374        }
375        if (!match && m_includeParents) {
376            // since parents can only be folders, check it only for folders
377            if (lock.getResourceName().endsWith("/")) {
378                match = rootPath.startsWith(lock.getResourceName());
379            }
380        }
381        if (match && (m_projectId != null) && !m_projectId.isNullUUID() && (lock.getProjectId() != null)) {
382            match = lock.getProjectId().equals(m_projectId);
383        }
384        if (match && (m_ownedByUserId != null) && !m_ownedByUserId.isNullUUID()) {
385            match = lock.getUserId().equals(m_ownedByUserId);
386        }
387        if (match && (m_notOwnedByUserId != null) && !m_notOwnedByUserId.isNullUUID()) {
388            match = !lock.getUserId().equals(m_notOwnedByUserId);
389        }
390        if (match && (m_lockableByUser != null)) {
391            match = lock.isLockableBy(m_lockableByUser);
392        }
393        if (match && (m_notLockableByUser != null)) {
394            match = !lock.isLockableBy(m_notLockableByUser);
395        }
396        if (match && !m_types.isEmpty()) {
397            match = m_types.contains(lock.getType());
398            match = match || (m_includeParents && lock.isInherited());
399        }
400        // check the related lock if available
401        if (!match && !lock.getRelatedLock().isNullLock()) {
402            if (!isRelatedCheck) {
403                match = match(rootPath, lock.getRelatedLock(), true);
404            } else {
405                LOG.warn("Invalid lock state for locks (" + lock + ", " + lock.getRelatedLock() + ")");
406            }
407        }
408        return match;
409    }
410}