001/*
002 *  Copyright 2018 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.plugins.repository.data.repositorydata.impl;
017
018import java.io.InputStream;
019import java.util.ArrayList;
020import java.util.Calendar;
021import java.util.LinkedHashMap;
022import java.util.LinkedHashSet;
023import java.util.List;
024import java.util.Map;
025import java.util.Set;
026
027import javax.jcr.Binary;
028import javax.jcr.Node;
029import javax.jcr.NodeIterator;
030import javax.jcr.PathNotFoundException;
031import javax.jcr.Property;
032import javax.jcr.PropertyIterator;
033import javax.jcr.PropertyType;
034import javax.jcr.RepositoryException;
035import javax.jcr.Session;
036import javax.jcr.Value;
037import javax.jcr.lock.Lock;
038import javax.jcr.lock.LockManager;
039
040import org.apache.commons.lang3.StringUtils;
041
042import org.ametys.plugins.repository.AmetysObjectResolver;
043import org.ametys.plugins.repository.AmetysRepositoryException;
044import org.ametys.plugins.repository.RepositoryConstants;
045import org.ametys.plugins.repository.data.UnknownDataException;
046import org.ametys.plugins.repository.data.repositorydata.ModifiableRepositoryData;
047import org.ametys.plugins.repository.data.repositorydata.RepositoryData;
048import org.ametys.plugins.repository.jcr.NodeHelper;
049import org.ametys.plugins.repository.jcr.NodeTypeHelper;
050
051/**
052 * Class for data values management in JCR repository
053 */
054public class JCRRepositoryData implements ModifiableRepositoryData
055{
056    private static final Map<Integer, String> __DATA_TYPES;
057    
058    static
059    {
060        __DATA_TYPES = new LinkedHashMap<>(6);
061        __DATA_TYPES.put(PropertyType.STRING, RepositoryData.STRING_REPOSITORY_DATA_TYPE);
062        __DATA_TYPES.put(PropertyType.LONG, RepositoryData.LONG_REPOSITORY_DATA_TYPE);
063        __DATA_TYPES.put(PropertyType.DOUBLE, RepositoryData.DOUBLE_REPOSITORY_DATA_TYPE);
064        __DATA_TYPES.put(PropertyType.BOOLEAN, RepositoryData.BOOLEAN_REPOSITORY_DATA_TYPE);
065        __DATA_TYPES.put(PropertyType.DATE, RepositoryData.CALENDAR_REPOSITORY_DATA_TYPE);
066        __DATA_TYPES.put(PropertyType.BINARY, RepositoryData.STREAM_REPOSITORY_DATA_TYPE);
067    }
068    
069    private Node _node;
070    private boolean _lockAlreadyChecked;
071    private String _defaultPrefix;
072    
073    /**
074     * Creates a JCR repository data from the given node
075     * @param node the Node supporting this repository data
076     */
077    public JCRRepositoryData(Node node)
078    {
079        this(node, RepositoryConstants.NAMESPACE_PREFIX);
080    }
081    
082    /**
083     * Creates a JCR repository data from the given node
084     * @param node the Node supporting this repository data
085     * @param defaultPrefix prefix to use for properties and child nodes
086     */
087    public JCRRepositoryData(Node node, String defaultPrefix)
088    {
089        _node = node;
090        _defaultPrefix = defaultPrefix;
091    }
092
093    public String getString(String name, String prefix)
094    {
095        try
096        {
097            Value value = _getValue(name, prefix);
098            return value.getString();
099        }
100        catch (RepositoryException e)
101        {
102            throw new AmetysRepositoryException("Unable to get the value of string data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
103        }
104    }
105    
106    public String[] getStrings(String name, String prefix)
107    {
108        try
109        {
110            Value[] values = _getValues(name, prefix);
111            String[] results = new String[values.length];
112            
113            for (int i = 0; i < values.length; i++)
114            {
115                Value value = values[i];
116                results[i] = value.getString();
117            }
118          
119            return results;
120        }
121        catch (RepositoryException e)
122        {
123            throw new AmetysRepositoryException("Unable to get the values of multiple string data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
124        }
125    }
126    
127    public Calendar getDate(String name, String prefix)
128    {
129        try
130        {
131            Value value = _getValue(name, prefix);
132            return value.getDate();
133        }
134        catch (RepositoryException e)
135        {
136            throw new AmetysRepositoryException("Unable to get the value of date data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
137        }
138    }
139
140    public Calendar[] getDates(String name, String prefix)
141    {
142        try
143        {
144            Value[] values = _getValues(name, prefix);
145            Calendar[] results = new Calendar[values.length];
146
147            for (int i = 0; i < values.length; i++)
148            {
149                Value value = values[i];
150                results[i] = value.getDate();
151            }
152          
153            return results;
154        }
155        catch (RepositoryException e)
156        {
157            throw new AmetysRepositoryException("Unable to get the values of multiple date data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
158        }
159    }
160    
161    public Long getLong(String name, String prefix)
162    {
163        try
164        {
165            Value value = _getValue(name, prefix);
166            return value.getLong();
167        }
168        catch (RepositoryException e)
169        {
170            throw new AmetysRepositoryException("Unable to get the value on long data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
171        }
172    }
173    
174    public Long[] getLongs(String name, String prefix)
175    {
176        try
177        {
178            Value[] values = _getValues(name, prefix);
179            Long[] results = new Long[values.length];
180            
181            for (int i = 0; i < values.length; i++)
182            {
183                Value value = values[i];
184                results[i] = value.getLong();
185            }
186          
187            return results;
188        }
189        catch (RepositoryException e)
190        {
191            throw new AmetysRepositoryException("Unable to get the values of multiple long data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
192        }
193    }
194    
195    public Double getDouble(String name, String prefix)
196    {
197        try
198        {
199            Value value = _getValue(name, prefix);
200            return value.getDouble();
201        }
202        catch (RepositoryException e)
203        {
204            throw new AmetysRepositoryException("Unable to get the value of double data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
205        }
206    }
207    
208    public Double[] getDoubles(String name, String prefix)
209    {
210        try
211        {
212            Value[] values = _getValues(name, prefix);
213            Double[] results = new Double[values.length];
214            
215            for (int i = 0; i < values.length; i++)
216            {
217                Value value = values[i];
218                results[i] = value.getDouble();
219            }
220          
221            return results;
222        }
223        catch (RepositoryException e)
224        {
225            throw new AmetysRepositoryException("Unable to get the values of multiple double data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
226        }
227    }
228    
229    public Boolean getBoolean(String name, String prefix)
230    {
231        try
232        {
233            Value value = _getValue(name, prefix);
234            return value.getBoolean();
235        }
236        catch (RepositoryException e)
237        {
238            throw new AmetysRepositoryException("Unable to get the value of boolean data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
239        }
240    }
241    
242    public Boolean[] getBooleans(String name, String prefix)
243    {
244        try
245        {
246            Value[] values = _getValues(name, prefix);
247            Boolean[] results = new Boolean[values.length];
248            
249            for (int i = 0; i < values.length; i++)
250            {
251                Value value = values[i];
252                results[i] = value.getBoolean();
253            }
254          
255            return results;
256        }
257        catch (RepositoryException e)
258        {
259            throw new AmetysRepositoryException("Unable to get the values of multiple boolean data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
260        }
261    }
262    
263    public InputStream[] getStreams(String name, String prefix)
264    {
265        try
266        {
267            Value[] values = _getValues(name, prefix);
268            InputStream[] results = new InputStream[values.length];
269            
270            for (int i = 0; i < values.length; i++)
271            {
272                Value value = values[i];
273                results[i] = value.getBinary().getStream();
274            }
275          
276            return results;
277        }
278        catch (RepositoryException e)
279        {
280            throw new AmetysRepositoryException("Unable to get the values of multiple stream data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
281        }
282    }
283    
284    public InputStream getStream(String name, String prefix)
285    {
286        try
287        {
288            Value value = _getValue(name, prefix);
289            return value.getBinary().getStream();
290        }
291        catch (RepositoryException e)
292        {
293            throw new AmetysRepositoryException("Unable to get the value of stream data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
294        }
295    }
296    
297    public Long getStreamLength(String name, String prefix)
298    {
299        try
300        {
301            return _node.getProperty(_getFullName(name, prefix)).getLength();
302        }
303        catch (RepositoryException e)
304        {
305            throw new AmetysRepositoryException("Unable to get the length of the value of stream data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
306        }
307    }
308    
309    private Value _getValue(String name, String prefix)
310    {
311        try
312        {
313            Property property = _node.getProperty(_getFullName(name, prefix));
314
315            if (property.getDefinition().isMultiple())
316            {
317                Value[] values = property.getValues();
318
319                if (values.length > 0)
320                {
321                    return values[0];
322                }
323                else
324                {
325                    throw new AmetysRepositoryException("Cannot get the value of data '" + _getFullName(name, prefix) + "' from an empty array in repository data at path '" + this + "'.");
326                }
327            }
328
329            return property.getValue();
330        }
331        catch (PathNotFoundException e)
332        {
333            throw new UnknownDataException("Unknown data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
334        }
335        catch (RepositoryException e)
336        {
337            throw new AmetysRepositoryException("Unable to get the value of data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
338        }
339    }
340    
341    private Value[] _getValues(String name, String prefix)
342    {
343        try
344        {
345            Property property = _node.getProperty(_getFullName(name, prefix));
346
347            if (property.getDefinition().isMultiple())
348            {
349                return property.getValues();
350            }
351
352            Value value = property.getValue();
353            return new Value[] {value};
354        }
355        catch (PathNotFoundException e)
356        {
357            throw new UnknownDataException("Unknown data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
358        }
359        catch (RepositoryException e)
360        {
361            throw new AmetysRepositoryException("Unable to get the multiple value of data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
362        }
363    }
364    
365    public ModifiableRepositoryData getRepositoryData(String name, String prefix)
366    {
367        try
368        {
369            if (_node.hasNode(_getFullName(name, prefix)))
370            {
371                Node node = _node.getNode(_getFullName(name, prefix));
372                return new JCRRepositoryData(node);
373            }
374            else
375            {
376                throw new UnknownDataException("Unknown repository data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.");
377            }
378        }
379        catch (RepositoryException e)
380        {
381            throw new AmetysRepositoryException("Unable to get the repository data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
382        }
383    }
384    
385    public RepositoryData[] getAllRepositoryData(String name, String prefix)
386    {
387        try
388        {
389            if (_node.hasNode(_getFullName(name, prefix)))
390            {
391                List<RepositoryData> results = new ArrayList<>();
392                NodeIterator nodeIterator = _node.getNodes(_getFullName(name, prefix));
393                
394                while (nodeIterator.hasNext())
395                {
396                    results.add(new JCRRepositoryData((Node) nodeIterator.next()));
397                }
398                
399                return results.toArray(new RepositoryData[results.size()]);
400            }
401            else
402            {
403                throw new UnknownDataException("Unknown repository data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.");
404            }
405        }
406        catch (RepositoryException e)
407        {
408            throw new AmetysRepositoryException("Unable to get the repository data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
409        }
410    }
411
412    public Set<String> getDataNames(String prefix) throws AmetysRepositoryException
413    {
414        final String finalPrefix;
415        if (prefix == null)
416        {
417            finalPrefix = "*:";
418        }
419        else if (StringUtils.isEmpty(prefix))
420        {
421            finalPrefix = prefix;
422        }
423        else
424        {
425            finalPrefix = prefix + ":";
426        }
427        
428        try
429        {
430            PropertyIterator propertiesIterator = _node.getProperties(finalPrefix + "*");
431            NodeIterator itNode = _node.getNodes(finalPrefix + "*");
432
433            Set<String> names = new LinkedHashSet<>();
434
435            while (propertiesIterator.hasNext())
436            {
437                Property property = propertiesIterator.nextProperty();
438                String propertyName = property.getName();
439                
440                if (!StringUtils.isEmpty(finalPrefix) || !propertyName.contains(":"))
441                {
442                    names.add(propertyName.substring(finalPrefix.length()));
443                }
444            }
445
446            while (itNode.hasNext())
447            {
448                Node node = itNode.nextNode();
449                
450                // Filtering out AmetysObjects: they're not data (except users).
451                if (!NodeTypeHelper.isNodeType(node, AmetysObjectResolver.OBJECT_TYPE) || NodeTypeHelper.isNodeType(node, RepositoryConstants.USER_NODETYPE))
452                {
453                    names.add(node.getName().substring(finalPrefix.length()));
454                }
455            }
456
457            return names;
458        }
459        catch (RepositoryException e)
460        {
461            throw new AmetysRepositoryException("Unable to get the data names of the repository data at path '" + this + "'.", e);
462        }
463    }
464    
465    public String getName()
466    {
467        try
468        {
469            return getName(_node);
470        }
471        catch (RepositoryException e)
472        {
473            throw new AmetysRepositoryException("Unable to get the name of the repository data at path '" + this + "'.", e);
474        }
475    }
476    
477    /**
478     * Retrieves the name of the given node, excluding its prefix
479     * @param node the node
480     * @return the name of the given node
481     * @throws RepositoryException if an error occurs
482     */
483    protected String getName(Node node) throws RepositoryException
484    {
485        String fullName = node.getName();
486        int prefixLength = fullName.indexOf(":") + 1;
487        return prefixLength > 0 ? fullName.substring(prefixLength) : fullName;
488    }
489    
490    public boolean hasValue(String name, String prefix)
491    {
492        try
493        {
494            return _node.hasProperty(_getFullName(name, prefix)) || _node.hasNode(_getFullName(name, prefix));
495        }
496        catch (RepositoryException e)
497        {
498            throw new AmetysRepositoryException("Unable to determine if there is a value for data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
499        }
500    }
501    
502    public String getType(String name, String prefix) throws UnknownDataException
503    {
504        try
505        {
506            String dataName = _getFullName(name, prefix);
507            
508            if (_node.hasProperty(dataName))
509            {
510                Property property = _node.getProperty(dataName);
511                int jcrType = property.getType();
512                
513                String dataType = __DATA_TYPES.get(jcrType);
514                if (dataType == null)
515                {
516                    throw new AmetysRepositoryException("Unable to get the type of data '" + dataName + "' in repository data at path '" + this + "'.");
517                }
518                
519                return dataType;
520            }
521            
522            if (_node.hasNode(dataName))
523            {
524                return NodeTypeHelper.getNodeTypeName(_node.getNode(dataName));
525            }
526            
527            throw new UnknownDataException("No data found for '" + dataName + "' in repository data at path '" + this + "'.");
528        }
529        catch (RepositoryException e)
530        {
531            throw new AmetysRepositoryException("Unable to get the type of data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
532        }
533    }
534
535    public boolean isMultiple(String name, String prefix) throws UnknownDataException
536    {
537        try
538        {
539            String dataName = _getFullName(name, prefix);
540            
541            if (_node.hasProperty(dataName))
542            {
543                Property property = _node.getProperty(dataName);
544    
545                if (property.getDefinition().isMultiple())
546                {
547                    return true;
548                }
549    
550                return false;
551            }
552            
553            if (_node.hasNode(dataName))
554            {
555                String nodeTypeName = NodeTypeHelper.getNodeTypeName(_node.getNode(dataName));
556                return RepositoryConstants.MULTIPLE_ITEM_NODETYPE.equals(nodeTypeName);
557            }
558            
559            throw new UnknownDataException("No data found for '" + dataName + "' in repository data at path '" + this + "'.");
560        }
561        catch (RepositoryException e)
562        {
563            throw new AmetysRepositoryException("Unable to determine if data '" + _getFullName(name, prefix) + "' is multiple in repository data at path '" + this + "'.", e);
564        }
565    }
566    
567    public void rename(String newName, String prefix)
568    {
569        NodeHelper.rename(_node, prefix + ":" + newName);
570    }
571    
572    public void setValue(String name, String value, String prefix)
573    {
574        if (value == null)
575        {
576            throw new NullPointerException("Unable to set a 'null' data for '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.");
577        }
578
579        _checkDataName(name, prefix);
580
581        try
582        {
583            _checkLock();
584            _node.setProperty(_getFullName(name, prefix), value);
585        }
586        catch (RepositoryException e)
587        {
588            throw new AmetysRepositoryException("Unable to set the value '" + value + "' for data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
589        }
590    }
591    
592    public void setValues(String name, String[] values, String prefix)
593    {
594        if (values == null)
595        {
596            throw new NullPointerException("Unable to set a 'null' data for '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.");
597        }
598
599        _checkDataName(name, prefix);
600
601        try
602        {
603            _checkLock();
604            _node.setProperty(_getFullName(name, prefix), values);
605        }
606        catch (RepositoryException e)
607        {
608            throw new AmetysRepositoryException("Unable to set the values '" + values + "' for data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
609        }
610    }
611    
612    public void setValue(String name, Calendar value, String prefix)
613    {
614        if (value == null)
615        {
616            throw new NullPointerException("Unable to set a 'null' data for '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.");
617        }
618
619        _checkDataName(name, prefix);
620
621        try
622        {
623            _checkLock();
624            _node.setProperty(_getFullName(name, prefix), value);
625        }
626        catch (RepositoryException e)
627        {
628            throw new AmetysRepositoryException("Unable to set the value '" + value + "' for data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
629        }
630    }
631    
632    public void setValues(String name, Calendar[] values, String prefix)
633    {
634        if (values == null)
635        {
636            throw new NullPointerException("Unable to set a 'null' data for '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.");
637        }
638
639        _checkDataName(name, prefix);
640
641        try
642        {
643            _checkLock();
644            
645            Value[] jcrValues = new Value[values.length];
646            for (int i = 0; i < values.length; i++)
647            {
648                Calendar value = values[i];
649                jcrValues[i] = _node.getSession().getValueFactory().createValue(value);
650            }
651            
652            _node.setProperty(_getFullName(name, prefix), jcrValues, PropertyType.DATE);
653        }
654        catch (RepositoryException e)
655        {
656            throw new AmetysRepositoryException("Unable to set the values '" + values + "' for data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
657        }
658    }
659    
660    public void setValue(String name, Long value, String prefix)
661    {
662        if (value == null)
663        {
664            throw new NullPointerException("Unable to set a 'null' data for '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.");
665        }
666
667        _checkDataName(name, prefix);
668
669        try
670        {
671            _checkLock();
672            _node.setProperty(_getFullName(name, prefix), value);
673        }
674        catch (RepositoryException e)
675        {
676            throw new AmetysRepositoryException("Unable to set the value '" + value + "' for data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
677        }
678    }
679    
680    public void setValues(String name, Long[] values, String prefix)
681    {
682        if (values == null)
683        {
684            throw new NullPointerException("Unable to set a 'null' data for '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.");
685        }
686
687        _checkDataName(name, prefix);
688
689        try
690        {
691            _checkLock();
692            
693            Value[] jcrValues = new Value[values.length];
694            for (int i = 0; i < values.length; i++)
695            {
696                long value = values[i];
697                jcrValues[i] = _node.getSession().getValueFactory().createValue(value);
698            }
699            
700            _node.setProperty(_getFullName(name, prefix), jcrValues, PropertyType.LONG);
701        }
702        catch (RepositoryException e)
703        {
704            throw new AmetysRepositoryException("Unable to set the values '" + values + "' for data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
705        }
706    }
707    
708    public void setValue(String name, Double value, String prefix)
709    {
710        if (value == null)
711        {
712            throw new NullPointerException("Unable to set a 'null' data for '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.");
713        }
714
715        _checkDataName(name, prefix);
716
717        try
718        {
719            _checkLock();
720            _node.setProperty(_getFullName(name, prefix), value);
721        }
722        catch (RepositoryException e)
723        {
724            throw new AmetysRepositoryException("Unable to set the value '" + value + "' for data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
725        }
726    }
727    
728    public void setValues(String name, Double[] values, String prefix)
729    {
730        if (values == null)
731        {
732            throw new NullPointerException("Unable to set a 'null' data for '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.");
733        }
734
735        _checkDataName(name, prefix);
736
737        try
738        {
739            _checkLock();
740            
741            Value[] jcrValues = new Value[values.length];
742            for (int i = 0; i < values.length; i++)
743            {
744                double value = values[i];
745                jcrValues[i] = _node.getSession().getValueFactory().createValue(value);
746            }
747            
748            _node.setProperty(_getFullName(name, prefix), jcrValues, PropertyType.DOUBLE);
749        }
750        catch (RepositoryException e)
751        {
752            throw new AmetysRepositoryException("Unable to set the values '" + values + "' for data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
753        }
754    }
755    
756    public void setValue(String name, Boolean value, String prefix)
757    {
758        if (value == null)
759        {
760            throw new NullPointerException("Unable to set a 'null' data for '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.");
761        }
762
763        _checkDataName(name, prefix);
764
765        try
766        {
767            _checkLock();
768            _node.setProperty(_getFullName(name, prefix), value);
769        }
770        catch (RepositoryException e)
771        {
772            throw new AmetysRepositoryException("Unable to set the value '" + value + "' for data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
773        }
774    }
775    
776    public void setValues(String name, Boolean[] values, String prefix)
777    {
778        if (values == null)
779        {
780            throw new NullPointerException("Unable to set a 'null' data for '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.");
781        }
782
783        _checkDataName(name, prefix);
784
785        try
786        {
787            _checkLock();
788            
789            Value[] jcrValues = new Value[values.length];
790            for (int i = 0; i < values.length; i++)
791            {
792                boolean value = values[i];
793                jcrValues[i] = _node.getSession().getValueFactory().createValue(value);
794            }
795            
796            _node.setProperty(_getFullName(name, prefix), jcrValues, PropertyType.BOOLEAN);
797        }
798        catch (RepositoryException e)
799        {
800            throw new AmetysRepositoryException("Unable to set the values '" + values + "' for data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
801        }
802    }
803    
804    public void setValue(String name, InputStream value, String prefix)
805    {
806        if (value == null)
807        {
808            throw new NullPointerException("Unable to set a 'null' data for '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.");
809        }
810
811        _checkDataName(name, prefix);
812
813        try
814        {
815            _checkLock();
816            Binary binary = _node.getSession().getValueFactory().createBinary(value);
817            _node.setProperty(_getFullName(name, prefix), binary);
818        }
819        catch (RepositoryException e)
820        {
821            throw new AmetysRepositoryException("Unable to set the value '" + value + "' for data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
822        }
823    }
824    
825    public void setValues(String name, InputStream[] values, String prefix)
826    {
827        if (values == null)
828        {
829            throw new NullPointerException("Unable to set a 'null' data for '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.");
830        }
831
832        _checkDataName(name, prefix);
833
834        try
835        {
836            _checkLock();
837            
838            Value[] jcrValues = new Value[values.length];
839            for (int i = 0; i < values.length; i++)
840            {
841                InputStream value = values[i];
842                Binary binary = _node.getSession().getValueFactory().createBinary(value);
843                jcrValues[i] = _node.getSession().getValueFactory().createValue(binary);
844            }
845            
846            _node.setProperty(_getFullName(name, prefix), jcrValues, PropertyType.BINARY);
847        }
848        catch (RepositoryException e)
849        {
850            throw new AmetysRepositoryException("Unable to set the values '" + values + "' for data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
851        }
852    }
853    
854    public ModifiableRepositoryData addRepositoryData(String name, String dataTypeName, String prefix)
855    {
856        try
857        {
858            _checkLock();
859            Node node = _node.addNode(_getFullName(name, prefix), dataTypeName);
860            return new JCRRepositoryData(node);
861        }
862        catch (RepositoryException e)
863        {
864            throw new AmetysRepositoryException("Unable to create repository data '" + _getFullName(name, prefix) + "' with type '" + dataTypeName + "' in repository data at path '" + this + "'.", e);
865        }
866    }
867
868    private void _checkDataName(String name, String prefix)
869    {
870        // Name null
871        if (name == null)
872        {
873            throw new AmetysRepositoryException("Unable to set a value with the invalid data name (null) in repository data at path '" + this + "'.");
874        }
875
876        // Valid name
877        if (!DATA_NAME_PATTERN.matcher(name).matches())
878        {
879            throw new AmetysRepositoryException("Unable to set a value with the invalid data name '" + name + "'. Only [a-zA-Z][a-zA-Z0-9-_]* are allowed.");
880        }
881        
882        // Has value
883        if (hasValue(name, prefix))
884        {
885            // Remove existing value to avoid values with same name (node + property)
886            removeValue(name, prefix);
887        }
888    }
889    
890    public void removeValue(String name, String prefix) throws UnknownDataException
891    {
892        try
893        {
894            _checkLock();
895            String dataName = _getFullName(name, prefix);
896            
897            if (_node.hasProperty(dataName))
898            {
899                _node.getProperty(dataName).remove();
900            }
901            else if (_node.hasNode(dataName))
902            {
903                // Remove all existing nodes
904                NodeIterator it = _node.getNodes(dataName);
905                while (it.hasNext())
906                {
907                    Node next = it.nextNode();
908                    next.remove();
909                }
910            }
911            else
912            {
913                throw new UnknownDataException("No data found for '" + dataName + "' in repository data at path '" + this + "'.");
914            }
915        }
916        catch (RepositoryException e)
917        {
918            throw new AmetysRepositoryException("Unable to remove data '" + _getFullName(name, prefix) + "' in repository data at path '" + this + "'.", e);
919        }
920    }
921    
922    private String _getFullName(String name, String prefix)
923    {
924        return StringUtils.isEmpty(prefix) ? name : prefix + ":" + name;
925    }
926
927    private void _checkLock() throws RepositoryException
928    {
929        if (!_lockAlreadyChecked && _node.isLocked())
930        {
931            LockManager lockManager = _node.getSession().getWorkspace().getLockManager();
932            
933            Lock lock = lockManager.getLock(_node.getPath());
934            Node lockHolder = lock.getNode();
935            
936            lockManager.addLockToken(lockHolder.getProperty(RepositoryConstants.METADATA_LOCKTOKEN).getString());
937            _lockAlreadyChecked = true;
938        }
939    }
940    
941    /**
942     * Retrieves the underlying node.
943     * @return the underlying node.
944     */
945    public Node getNode()
946    {
947        return _node;
948    }
949    
950    /**
951     * Retrieves the current session
952     * @return he session
953     * @throws AmetysRepositoryException if an error occurs
954     */
955    public Session getSession() throws AmetysRepositoryException
956    {
957        try
958        {
959            return _node.getSession();
960        }
961        catch (RepositoryException e)
962        {
963            throw new AmetysRepositoryException("unable to retrieve the session of the repository data at path '" + this + "'.", e);
964        }
965    }
966    
967    public String getDefaultPrefix()
968    {
969        return _defaultPrefix;
970    }
971    
972    @Override
973    public String toString()
974    {
975        try
976        {
977            return _node.getPath() + " (" + _node.getIdentifier() + ")";
978        }
979        catch (RepositoryException e)
980        {
981            throw new AmetysRepositoryException("Unable to get infos about the repository data of node '" + _node + "'.", e);
982        }
983    }
984}