001/*
002 *  Copyright 2010 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.cms.support;
017
018import javax.jcr.Item;
019import javax.jcr.Node;
020import javax.jcr.Property;
021import javax.jcr.RepositoryException;
022import javax.jcr.version.OnParentVersionAction;
023
024import org.apache.commons.collections.Predicate;
025import org.apache.commons.collections.PredicateUtils;
026import org.apache.commons.lang.ArrayUtils;
027import org.apache.jackrabbit.JcrConstants;
028
029import org.ametys.plugins.repository.RepositoryConstants;
030import org.ametys.plugins.repository.jcr.NodeTypeHelper;
031
032/**
033 * Provides the following predicates:
034 * <ul>
035 * <li>propertyNamePredicate - accept a property depending on his name.
036 * <li>nodeNamePredicate - accept a node depending on his name.
037 * <li>nodeTypePredicate - accept a node depending on his node type.
038 * </ul>
039 */
040public class AmetysPredicateUtils extends PredicateUtils
041{
042    private static AllowedForLivePredicate _allowedLivePredicate = new  AllowedForLivePredicate();
043    
044    /**
045     * Tests if the property has the following name.
046     * @param propertyName the property name.
047     * @return <code>true</code> if the property has the given name,
048     *         <code>false</code> otherwise.
049     */
050    public static Predicate propertyNamePredicate(final String propertyName)
051    {
052        return new Predicate()
053        {
054            @Override
055            public boolean evaluate(Object object)
056            {
057                if (object instanceof Property)
058                {
059                    try
060                    {
061                        return ((Property) object).getName().equals(propertyName);
062                    }
063                    catch (RepositoryException e)
064                    {
065                        throw new RuntimeException("Unable to get property name", e);
066                    }
067                }
068                
069                return false;
070            }
071        };
072    }
073    
074    /**
075     * Tests if the property has a given name in a list.
076     * @param propertyNames one or several property names.
077     * @return <code>true</code> if the property has on of the given names,
078     *         <code>false</code> otherwise.
079     */
080    public static Predicate propertyNamesPredicate(final String ... propertyNames)
081    {
082        return new Predicate()
083        {
084            @Override
085            public boolean evaluate(Object object)
086            {
087                if (object instanceof Property)
088                {
089                    try
090                    {
091                        String propName = ((Property) object).getName();
092                        return ArrayUtils.contains(propertyNames, propName);
093                    }
094                    catch (RepositoryException e)
095                    {
096                        throw new RuntimeException("Unable to get property name", e);
097                    }
098                }
099                return false;
100            }
101        };
102    }
103    
104    /**
105     * Tests if the property is not a protected one (e.g. jcr:uuid).
106     * @param predicate the additional predicate to evaluate.
107     * @return <code>true</code> if the property has the given name,
108     *         <code>false</code> otherwise.
109     */
110    public static Predicate ignoreProtectedProperties(final Predicate predicate)
111    {
112        return new IgnoreProtectedPredicate(predicate);
113    }
114    
115    /**
116     * Tests if the node has the following name.
117     * @param nodeName the node name.
118     * @return <code>true</code> if the node has the given name,
119     *         <code>false</code> otherwise.
120     */
121    public static Predicate nodeNamePredicate(final String nodeName)
122    {
123        return new Predicate()
124        {
125            @Override
126            public boolean evaluate(Object object)
127            {
128                if (object instanceof Node)
129                {
130                    try
131                    {
132                        return ((Node) object).getName().equals(nodeName);
133                    }
134                    catch (RepositoryException e)
135                    {
136                        throw new RuntimeException("Unable to get node name", e);
137                    }
138                }
139                
140                return false;
141            }
142        };
143    }
144    
145    /**
146     * Tests if the node or property is a Ametys attribute, ie. it is prefixed by 'ametys:'
147     * @return <code>true</code> if the name starts with 'ametys:'
148     *         <code>false</code> otherwise.
149     */
150    public static Predicate ametysAttributePredicate()
151    {
152        return new Predicate()
153        {
154            @Override
155            public boolean evaluate(Object object)
156            {
157                if (object instanceof Item)
158                {
159                    try
160                    {
161                        return ((Item) object).getName().startsWith(RepositoryConstants.NAMESPACE_PREFIX + ":");
162                    }
163                    catch (RepositoryException e)
164                    {
165                        throw new RuntimeException("Unable to get item name", e);
166                    }
167                }
168                
169                return false;
170            }
171        };
172    }
173    
174    /**
175     * Tests if the node is of the given node type.
176     * @param nodeTypeName the node type name.
177     * @return <code>true</code> if the node has the given node type,
178     *         <code>false</code> otherwise.
179     */
180    public static Predicate nodeTypePredicate(final String nodeTypeName)
181    {
182        return new Predicate()
183        {
184            @Override
185            public boolean evaluate(Object object)
186            {
187                if (object instanceof Node)
188                {
189                    try
190                    {
191                        return ((Node) object).isNodeType(nodeTypeName);
192                    }
193                    catch (RepositoryException e)
194                    {
195                        throw new RuntimeException("Unable to test node type", e);
196                    }
197                }
198                
199                return false;
200            }
201        };
202    }
203    
204    private static final class IgnoreProtectedPredicate implements Predicate
205    {
206        private Predicate _predicate;
207        
208        IgnoreProtectedPredicate(Predicate predicate)
209        {
210            _predicate = predicate;
211        }
212        
213        @Override
214        public boolean evaluate(Object object)
215        {
216            if (object instanceof Property)
217            {
218                Property property = (Property) object;
219                
220                try
221                {
222                    Node parentNode = property.getParent();
223                    String propertyName = property.getName();
224                    
225                    if (parentNode.isNodeType(JcrConstants.NT_FROZENNODE))
226                    {
227                        if (propertyName.startsWith("jcr:"))
228                        {
229                            if (!propertyName.equals(JcrConstants.JCR_ENCODING) && !propertyName.equals(JcrConstants.JCR_MIMETYPE)
230                                    && !propertyName.equals(JcrConstants.JCR_DATA) && !propertyName.equals(JcrConstants.JCR_LASTMODIFIED))
231                            {
232                                // Refuse potential protected property
233                                return false;
234                            }
235                        }
236                        
237                        return _predicate.evaluate(object);
238                    }
239                    else
240                    {
241                        // If property is not protected, do process it
242                        return !property.getDefinition().isProtected() && _predicate.evaluate(object);
243                    }
244                }
245                catch (RepositoryException e)
246                {
247                    throw new RuntimeException("Unable to process property", e);
248                }
249            }
250            
251            return false;
252        }
253    }
254    
255    /**
256     * Tests if the node or property is allowed in the live workspace
257     * @return <code>true</code> if the property or node is allowed,
258     *         <code>false</code> otherwise.
259     */
260    public static Predicate isAllowedForLiveContent()
261    {
262        return _allowedLivePredicate;
263    }
264    
265    private static final class AllowedForLivePredicate implements Predicate
266    {
267        AllowedForLivePredicate()
268        {
269        }
270        
271        @Override
272        public boolean evaluate(Object object)
273        {
274            if (object instanceof Property)
275            {
276               
277                Property property = (Property) object;
278                try
279                {
280                    // oswf:* item are not allowed
281                    return !property.getName().startsWith("oswf:")
282                            && !property.getName().equals(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":initial-content")
283                            && !property.getName().equals(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":workflowRef");
284                }
285                catch (RepositoryException e)
286                {
287                    throw new RuntimeException("Unable to process property", e);
288                }
289            }
290            else if (object instanceof Node)
291            {
292                Node node = (Node) object;
293                try
294                {
295                    String nodeType = NodeTypeHelper.getNodeTypeName(node);
296                    
297                    // oswf:* item are not allowed
298                    return !nodeType.startsWith("oswf:");
299                }
300                catch (RepositoryException e)
301                {
302                    throw new RuntimeException("Unable to process node", e);
303                }
304            }
305            
306            return false;
307        }
308    }
309    
310    
311    /**
312     * Tests if the node or property is allowed in the live workspace
313     * @param node The parent node.
314     * @return <code>true</code> if the property or node is allowed,
315     *         <code>false</code> otherwise.
316     */
317    public static Predicate isNonVersionned(Node node)
318    {
319        return new NonVersionnedPredicate(node);
320    }
321    
322    private static final class NonVersionnedPredicate implements Predicate
323    {
324        private Node _node;
325
326        NonVersionnedPredicate(Node node)
327        {
328            _node = node;
329        }
330        
331        @Override
332        public boolean evaluate(Object object)
333        {
334            if (object instanceof Property)
335            {
336                Property property = (Property) object;
337                
338                try
339                {
340                    if (!property.getParent().getIdentifier().equals(_node.getIdentifier()))
341                    {
342                        // The property must be one of a child node (of a non versionned node)
343                        return true;
344                    }
345                    return property.getDefinition().getOnParentVersion() == OnParentVersionAction.IGNORE;
346                }
347                catch (RepositoryException e)
348                {
349                    throw new RuntimeException("Unable to process property", e);
350                }
351            }
352            else if (object instanceof Node)
353            {
354                Node node = (Node) object;
355                try
356                {
357                    if (!node.getParent().getIdentifier().equals(_node.getIdentifier()))
358                    {
359                        // The node must be one of a child node (of a non versionned node)
360                        return true;
361                    }
362                    return node.getDefinition().getOnParentVersion() == OnParentVersionAction.IGNORE;
363                }
364                catch (RepositoryException e)
365                {
366                    throw new RuntimeException("Unable to process node", e);
367                }
368            }
369            
370            return false;
371        }
372    }
373}