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