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.plugins.repository.metadata.jcr;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.Date;
021import java.util.GregorianCalendar;
022import java.util.HashSet;
023import java.util.List;
024import java.util.Set;
025import java.util.regex.Pattern;
026
027import javax.jcr.Node;
028import javax.jcr.NodeIterator;
029import javax.jcr.PathNotFoundException;
030import javax.jcr.Property;
031import javax.jcr.PropertyIterator;
032import javax.jcr.PropertyType;
033import javax.jcr.RepositoryException;
034import javax.jcr.Value;
035import javax.jcr.lock.Lock;
036import javax.jcr.lock.LockManager;
037
038import org.ametys.core.user.UserIdentity;
039import org.ametys.plugins.repository.AmetysObjectResolver;
040import org.ametys.plugins.repository.AmetysRepositoryException;
041import org.ametys.plugins.repository.ModifiableTraversableAmetysObject;
042import org.ametys.plugins.repository.RepositoryConstants;
043import org.ametys.plugins.repository.jcr.NodeTypeHelper;
044import org.ametys.plugins.repository.metadata.CommentableCompositeMetadata;
045import org.ametys.plugins.repository.metadata.MetadataComment;
046import org.ametys.plugins.repository.metadata.ModifiableBinaryMetadata;
047import org.ametys.plugins.repository.metadata.ModifiableCompositeMetadata;
048import org.ametys.plugins.repository.metadata.ModifiableFile;
049import org.ametys.plugins.repository.metadata.ModifiableFolder;
050import org.ametys.plugins.repository.metadata.ModifiableResource;
051import org.ametys.plugins.repository.metadata.ModifiableRichText;
052import org.ametys.plugins.repository.metadata.UnknownMetadataException;
053
054/**
055 * JCR implementation of a CompositeMetadata.<br>
056 * This implementation is based on a child node of the Node supporting the actual content.
057 */
058public class JCRCompositeMetadata implements ModifiableCompositeMetadata, CommentableCompositeMetadata
059{
060    /** Prefix for jcr properties and nodes names */
061    public static final String METADATA_PREFIX = RepositoryConstants.NAMESPACE_PREFIX + ':';
062    
063    /** Comments metadata suffix */
064    public static final String METADATA_COMMENTS_SUFFIX = "_comments";
065    
066    /** The JCR nodetype of object collection metadatas. */
067    public static final String OBJECT_COLLECTION_METADATA_NODETYPE = METADATA_PREFIX + "objectCollectionMetadata";
068    
069    private static Pattern __metadataNamePattern;
070
071    private Node _node;
072    private boolean _lockAlreadyChecked;
073    private AmetysObjectResolver _resolver;
074
075    /**
076     * Constructor
077     * @param metadataNode the Node supporting this composite metadata
078     * @param resolver The resolver, used to resolve object collections.
079     */
080    public JCRCompositeMetadata(Node metadataNode, AmetysObjectResolver resolver)
081    {
082        _node = metadataNode;
083        _resolver = resolver;
084    }
085    
086    /**
087     * Retrieves the underlying node.
088     * @return the underlying node.
089     */
090    public Node getNode()
091    {
092        return _node;
093    }
094    
095    public boolean hasMetadata(String metadataName) throws AmetysRepositoryException
096    {
097        try
098        {
099            // TODO filter sub-nodes by type (like in getMetadataNames).
100            return _node.hasProperty(METADATA_PREFIX + metadataName) || _node.hasNode(METADATA_PREFIX + metadataName);
101        }
102        catch (RepositoryException e)
103        {
104            throw new AmetysRepositoryException(e);
105        }
106    }
107    
108    @Override
109    public void rename(String newName) throws AmetysRepositoryException
110    {
111        try
112        {
113            getNode().getSession().move(getNode().getPath(), getNode().getParent().getPath() + "/" + newName);
114        }
115        catch (RepositoryException e)
116        {
117            throw new AmetysRepositoryException(e);
118        }        
119    }
120
121    public void removeMetadata(String metadataName) throws AmetysRepositoryException
122    {
123        try
124        {
125            _checkLock();
126            String realMetadataName = METADATA_PREFIX + metadataName;
127            
128            if (_node.hasProperty(realMetadataName))
129            {
130                _node.getProperty(realMetadataName).remove();
131            }
132            else if (_node.hasNode(realMetadataName))
133            {
134                // Remove all existing nodes
135                NodeIterator it = _node.getNodes(realMetadataName);
136                while (it.hasNext())
137                {
138                    Node next = it.nextNode();
139                    next.remove();
140                }
141            }
142            else
143            {
144                throw new UnknownMetadataException("No metadata for name: " + metadataName);
145            }
146        }
147        catch (RepositoryException e)
148        {
149            throw new AmetysRepositoryException("Unable to remove metadata " + metadataName, e);
150        }
151    }
152    
153    @Override
154    public void copyTo(ModifiableCompositeMetadata metadata) throws AmetysRepositoryException
155    {
156        String[] metadataNames = getMetadataNames();
157        for (String metadataName : metadataNames)
158        {
159            if (hasMetadata(metadataName))
160            {
161                MetadataType type = getType(metadataName);
162                
163                boolean multiple = isMultiple(metadataName);
164                switch (type)
165                {
166                    case COMPOSITE:
167                        ModifiableCompositeMetadata compositeMetadata = metadata.getCompositeMetadata(metadataName, true);
168                        getCompositeMetadata(metadataName).copyTo(compositeMetadata);
169                        break;
170                    case USER:
171                        if (multiple)
172                        {
173                            metadata.setMetadata(metadataName, getUserArray(metadataName));
174                        }
175                        else
176                        {
177                            metadata.setMetadata(metadataName, getUser(metadataName));
178                        }
179                        break;
180                    case BOOLEAN:
181                        if (multiple)
182                        {
183                            metadata.setMetadata(metadataName, getBooleanArray(metadataName));
184                        }
185                        else
186                        {
187                            metadata.setMetadata(metadataName, getBoolean(metadataName));
188                        }
189                        break;
190                    case DATE:
191                        if (multiple)
192                        {
193                            metadata.setMetadata(metadataName, getDateArray(metadataName));
194                        }
195                        else
196                        {
197                            metadata.setMetadata(metadataName, getDate(metadataName));
198                        }
199                        break;
200                    case DOUBLE:
201                        if (multiple)
202                        {
203                            metadata.setMetadata(metadataName, getDoubleArray(metadataName));
204                        }
205                        else
206                        {
207                            metadata.setMetadata(metadataName, getDouble(metadataName));
208                        }
209                        break;
210                    case LONG:
211                        if (multiple)
212                        {
213                            metadata.setMetadata(metadataName, getLongArray(metadataName));
214                        }
215                        else
216                        {
217                            metadata.setMetadata(metadataName, getLong(metadataName));
218                        }
219                        break;
220                    case BINARY:
221                        ModifiableBinaryMetadata binaryMetadata = metadata.getBinaryMetadata(metadataName, true);
222                        ModifiableBinaryMetadata srcBinary = getBinaryMetadata(metadataName);
223                        
224                        binaryMetadata.setFilename(srcBinary.getFilename());
225                        binaryMetadata.setMimeType(srcBinary.getMimeType());
226                        binaryMetadata.setLastModified(srcBinary.getLastModified());
227                        binaryMetadata.setEncoding(srcBinary.getEncoding());
228                        binaryMetadata.setInputStream(srcBinary.getInputStream());
229                        break;
230                    case RICHTEXT:
231                        ModifiableRichText richText = metadata.getRichText(metadataName, true);
232                        ModifiableRichText srcRichText = getRichText(metadataName);
233                        
234                        richText.setInputStream(srcRichText.getInputStream());
235                        richText.setEncoding(srcRichText.getEncoding());
236                        richText.setLastModified(srcRichText.getLastModified());
237                        richText.setMimeType(srcRichText.getMimeType());
238                        
239                        // Copy additional data
240                        _copyDataFolder (srcRichText.getAdditionalDataFolder(), richText.getAdditionalDataFolder());
241                        break;
242                    default:
243                        if (multiple)
244                        {
245                            metadata.setMetadata(metadataName, getStringArray(metadataName));
246                        }
247                        else
248                        {
249                            metadata.setMetadata(metadataName, getString(metadataName));
250                        }
251                        break;
252                }
253            }
254        }
255    }
256    
257    private void _copyDataFolder (ModifiableFolder srcDataFolder, ModifiableFolder targetDataFolder)
258    {
259        // Copy folders
260        Collection<ModifiableFolder> folders = srcDataFolder.getFolders();
261        for (ModifiableFolder srcSubfolder : folders)
262        {
263            ModifiableFolder targetSubFolder = targetDataFolder.addFolder(srcSubfolder.getName());
264            _copyDataFolder (srcSubfolder, targetSubFolder);
265        }
266        
267        // Copy files
268        Collection<ModifiableFile> files = srcDataFolder.getFiles();
269        for (ModifiableFile srcFile : files)
270        {
271            _copyFile (srcFile, targetDataFolder);
272        }
273    }
274    
275    private void _copyFile (ModifiableFile srcFile, ModifiableFolder targetDataFolder)
276    {
277        ModifiableResource srcResource = srcFile.getResource();
278        
279        ModifiableFile targetFile = targetDataFolder.addFile(srcFile.getName());
280        ModifiableResource targetResource = targetFile.getResource();
281        
282        targetResource.setLastModified(srcResource.getLastModified());
283        targetResource.setMimeType(srcResource.getMimeType());
284        targetResource.setEncoding(srcResource.getEncoding());
285        targetResource.setInputStream(srcResource.getInputStream());
286    }
287
288    public MetadataType getType(String metadataName) throws AmetysRepositoryException
289    {
290        try
291        {
292            String realMetadataName = METADATA_PREFIX + metadataName;
293            
294            if (_node.hasNode(realMetadataName))
295            {
296                Node node = _node.getNode(realMetadataName);
297                String type = node.getPrimaryNodeType().getName();
298                if ("nt:frozenNode".equals(_node.getPrimaryNodeType().getName()))
299                {
300                    // on version history
301                    type = node.getProperty("jcr:frozenPrimaryType").getString();
302                }
303                
304                if (type.equals(OBJECT_COLLECTION_METADATA_NODETYPE))
305                {
306                    return MetadataType.OBJECT_COLLECTION;
307                }
308                else if (type.equals(METADATA_PREFIX + "binaryMetadata"))
309                {
310                    return MetadataType.BINARY;
311                }
312                else if (type.equals(METADATA_PREFIX + "richText"))
313                {
314                    return MetadataType.RICHTEXT;
315                }
316                else if (type.equals(METADATA_PREFIX + "user"))
317                {
318                    return MetadataType.USER;
319                }
320                else
321                {
322                    return MetadataType.COMPOSITE;
323                }
324            }
325
326            if (_node.hasProperty(realMetadataName))
327            {
328                Property property = _node.getProperty(realMetadataName);
329                int type = property.getType();
330    
331                switch (type)
332                {
333                    case PropertyType.STRING:
334                        return MetadataType.STRING;
335                    case PropertyType.BOOLEAN:
336                        return MetadataType.BOOLEAN;
337                    case PropertyType.DATE:
338                        return MetadataType.DATE;
339                    case PropertyType.DOUBLE:
340                        return MetadataType.DOUBLE;
341                    case PropertyType.LONG:
342                        return MetadataType.LONG;
343                    default:
344                        throw new AmetysRepositoryException("Unhandled JCR property type : " + PropertyType.nameFromValue(type));
345                }
346            }
347            
348            throw new UnknownMetadataException("No metadata for name: " + metadataName);
349        }
350        catch (RepositoryException e)
351        {
352            throw new AmetysRepositoryException("Unable to get property type", e);
353        }
354    }
355
356    public String getString(String metadataName)
357    {
358        return _getString(metadataName, null, true);
359    }
360
361    @Override
362    public String getString(String metadataName, String defaultValue)
363    {
364        return _getString(metadataName, defaultValue, false);
365    }
366    
367    private String _getString(String metadataName, String defaultValue, boolean failIfNotExist)
368    {
369        try
370        {
371            Property property = _node.getProperty(METADATA_PREFIX + metadataName);
372
373            if (property.getDefinition().isMultiple())
374            {
375                Value[] values = property.getValues();
376
377                if (values.length > 0)
378                {
379                    return values[0].getString();
380                }
381                else
382                {
383                    throw new AmetysRepositoryException("Cannot get a String from an empty array");
384                }
385            }
386
387            return property.getString();
388        }
389        catch (PathNotFoundException e)
390        {
391            if (failIfNotExist)
392            {
393                throw new UnknownMetadataException("Unknown metadata " + METADATA_PREFIX + metadataName + " of " + _node, e);
394            }
395            else
396            {
397                return defaultValue;
398            }
399        }
400        catch (RepositoryException e)
401        {
402            throw new AmetysRepositoryException("Unable to get String property", e);
403        }
404    }
405
406    public String[] getStringArray(String metadataName) throws AmetysRepositoryException
407    {
408        return _getStringArray(metadataName, null, true);
409    }
410
411    public String[] getStringArray(String metadataName, String[] defaultValues)
412    {
413        return _getStringArray(metadataName, defaultValues, false);
414    }
415    
416    private String[] _getStringArray(String metadataName, String[] defaultValues, boolean failIfNotExist)
417    {
418        try
419        {
420            Property property = _node.getProperty(METADATA_PREFIX + metadataName);
421
422            if (property.getDefinition().isMultiple())
423            {
424                Value[] values = property.getValues();
425
426                String[] results = new String[values.length];
427                for (int i = 0; i < values.length; i++)
428                {
429                    Value value = values[i];
430                    results[i] = value.getString();
431                }
432
433                return results;
434            }
435
436            Value value = property.getValue();
437            return new String[] {value.getString()};
438        }
439        catch (PathNotFoundException e)
440        {
441            if (failIfNotExist)
442            {
443                throw new UnknownMetadataException("Unknown metadata " + METADATA_PREFIX + metadataName + " of " + _node, e);
444            }
445            else
446            {
447                return defaultValues;
448            }
449        }
450        catch (RepositoryException e)
451        {
452            throw new AmetysRepositoryException("Unable to get String array property", e);
453        }
454    }
455
456    public boolean getBoolean(String metadataName) throws UnknownMetadataException, AmetysRepositoryException
457    {
458        return _getBoolean(metadataName, null, true);
459    }
460
461    public boolean getBoolean(String metadataName, boolean defaultValue) throws AmetysRepositoryException
462    {
463        return _getBoolean(metadataName, defaultValue, false);
464    }
465    
466    private boolean _getBoolean(String metadataName, Boolean defaultValue, boolean failIfNotExist)
467    {
468        try
469        {
470            Property property = _node.getProperty(METADATA_PREFIX + metadataName);
471
472            if (property.getDefinition().isMultiple())
473            {
474                Value[] values = property.getValues();
475
476                if (values.length > 0)
477                {
478                    return values[0].getBoolean();
479                }
480                else
481                {
482                    throw new AmetysRepositoryException("Cannot get a boolean from an empty array");
483                }
484            }
485
486            return property.getBoolean();
487        }
488        catch (PathNotFoundException e)
489        {
490            if (failIfNotExist)
491            {
492                throw new UnknownMetadataException("Unknown metadata " + METADATA_PREFIX + metadataName + " of " + _node, e);
493            }
494            else
495            {
496                return defaultValue;
497            }
498        }
499        catch (RepositoryException e)
500        {
501            throw new AmetysRepositoryException("Unable to get boolean property", e);
502        }
503    }
504
505    public boolean[] getBooleanArray(String metadataName) throws AmetysRepositoryException
506    {
507        return _getBooleanArray(metadataName, null, true);
508    }
509
510    public boolean[] getBooleanArray(String metadataName, boolean[] defaultValues)
511    {
512        return _getBooleanArray(metadataName, defaultValues, false);
513    }
514
515    private boolean[] _getBooleanArray(String metadataName, boolean[] defaultValues, boolean failIfNotExist)
516    {
517        try
518        {
519            Property property = _node.getProperty(METADATA_PREFIX + metadataName);
520
521            if (property.getDefinition().isMultiple())
522            {
523                Value[] values = property.getValues();
524
525                boolean[] results = new boolean[values.length];
526                for (int i = 0; i < values.length; i++)
527                {
528                    Value value = values[i];
529                    results[i] = value.getBoolean();
530                }
531
532                return results;
533            }
534
535            Value value = property.getValue();
536            return new boolean[] {value.getBoolean()};
537        }
538        catch (PathNotFoundException e)
539        {
540            if (failIfNotExist)
541            {
542                throw new UnknownMetadataException("Unknown metadata " + METADATA_PREFIX + metadataName + " of " + _node, e);
543            }
544            else
545            {
546                return defaultValues;
547            }
548        }
549        catch (RepositoryException e)
550        {
551            throw new AmetysRepositoryException("Unable to get boolean array property", e);
552        }
553    }
554
555    public Date getDate(String metadataName) throws AmetysRepositoryException
556    {
557        return _getDate(metadataName, null, true);
558    }
559
560    public Date getDate(String metadataName, Date defaultValue) throws AmetysRepositoryException
561    {
562        return _getDate(metadataName, defaultValue, false);
563    }
564
565    private Date _getDate(String metadataName, Date defaultValue, boolean failIfNotExist)
566    {
567        try
568        {
569            Property property = _node.getProperty(METADATA_PREFIX + metadataName);
570
571            if (property.getDefinition().isMultiple())
572            {
573                Value[] values = property.getValues();
574
575                if (values.length > 0)
576                {
577                    return values[0].getDate().getTime();
578                }
579                else
580                {
581                    throw new AmetysRepositoryException("Cannot get a date from an empty array");
582                }
583            }
584
585            return property.getDate().getTime();
586        }
587        catch (PathNotFoundException e)
588        {
589            if (failIfNotExist)
590            {
591                throw new UnknownMetadataException("Unknown metadata " + METADATA_PREFIX + metadataName + " of " + _node, e);
592            }
593            else
594            {
595                return defaultValue;
596            }
597        }
598        catch (RepositoryException e)
599        {
600            throw new AmetysRepositoryException("Unable to get date property", e);
601        }
602    }
603
604    public Date[] getDateArray(String metadataName) throws AmetysRepositoryException
605    {
606        return _getDateArray(metadataName, null, true);
607    }
608
609    public Date[] getDateArray(String metadataName, Date[] defaultValues)
610    {
611        return _getDateArray(metadataName, defaultValues, false);
612    }
613
614    private Date[] _getDateArray(String metadataName, Date[] defaultValues, boolean failIfNotExist)
615    {
616        try
617        {
618            Property property = _node.getProperty(METADATA_PREFIX + metadataName);
619
620            if (property.getDefinition().isMultiple())
621            {
622                Value[] values = property.getValues();
623
624                Date[] results = new Date[values.length];
625                for (int i = 0; i < values.length; i++)
626                {
627                    Value value = values[i];
628                    results[i] = value.getDate().getTime();
629                }
630
631                return results;
632            }
633
634            Value value = property.getValue();
635            return new Date[] {value.getDate().getTime()};
636        }
637        catch (PathNotFoundException e)
638        {
639            if (failIfNotExist)
640            {
641                throw new UnknownMetadataException("Unknown metadata " + METADATA_PREFIX + metadataName + " of " + _node, e);
642            }
643            else
644            {
645                return defaultValues;
646            }
647        }
648        catch (RepositoryException e)
649        {
650            throw new AmetysRepositoryException("Unable to get date array property", e);
651        }
652    }
653
654    public long getLong(String metadataName) throws AmetysRepositoryException
655    {
656        return _getLong(metadataName, null, true);
657    }
658
659    public long getLong(String metadataName, long defaultValue) throws AmetysRepositoryException
660    {
661        return _getLong(metadataName, defaultValue, false);
662    }
663
664    private long _getLong(String metadataName, Long defaultValue, boolean failIfNotExist)
665    {
666        try
667        {
668            Property property = _node.getProperty(METADATA_PREFIX + metadataName);
669
670            if (property.getDefinition().isMultiple())
671            {
672                Value[] values = property.getValues();
673
674                if (values.length > 0)
675                {
676                    return values[0].getLong();
677                }
678                else
679                {
680                    throw new AmetysRepositoryException("Cannot get a long from an empty array");
681                }
682            }
683
684            return property.getLong();
685        }
686        catch (PathNotFoundException e)
687        {
688            if (failIfNotExist)
689            {
690                throw new UnknownMetadataException("Unknown metadata " + METADATA_PREFIX + metadataName + " of " + _node, e);
691            }
692            else
693            {
694                return defaultValue;
695            }
696        }
697        catch (RepositoryException e)
698        {
699            throw new AmetysRepositoryException("Unable to get long property", e);
700        }
701    }
702
703    public long[] getLongArray(String metadataName) throws AmetysRepositoryException
704    {
705        return _getLongArray(metadataName, null, true);
706    }
707
708    public long[] getLongArray(String metadataName, long[] defaultValues)
709    {
710        return _getLongArray(metadataName, defaultValues, false);
711    }
712
713    private long[] _getLongArray(String metadataName, long[] defaultValues, boolean failIfNotExist)
714    {
715        try
716        {
717            Property property = _node.getProperty(METADATA_PREFIX + metadataName);
718
719            if (property.getDefinition().isMultiple())
720            {
721                Value[] values = property.getValues();
722
723                long[] results = new long[values.length];
724                for (int i = 0; i < values.length; i++)
725                {
726                    Value value = values[i];
727                    results[i] = value.getLong();
728                }
729
730                return results;
731            }
732
733            Value value = property.getValue();
734            return new long[] {value.getLong()};
735        }
736        catch (PathNotFoundException e)
737        {
738            if (failIfNotExist)
739            {
740                throw new UnknownMetadataException("Unknown metadata " + METADATA_PREFIX + metadataName + " of " + _node, e);
741            }
742            else
743            {
744                return defaultValues;
745            }
746        }
747        catch (RepositoryException e)
748        {
749            throw new AmetysRepositoryException("Unable to get long array property", e);
750        }
751    }
752
753    public double getDouble(String metadataName) throws AmetysRepositoryException
754    {
755        return _getDouble(metadataName, null, true);
756    }
757
758    public double getDouble(String metadataName, double defaultValue) throws AmetysRepositoryException
759    {
760        return _getDouble(metadataName, defaultValue, false);
761    }
762
763    private double _getDouble(String metadataName, Double defaultValue, boolean failIfNotExist)
764    {
765        try
766        {
767            Property property = _node.getProperty(METADATA_PREFIX + metadataName);
768
769            if (property.getDefinition().isMultiple())
770            {
771                Value[] values = property.getValues();
772
773                if (values.length > 0)
774                {
775                    return values[0].getDouble();
776                }
777                else
778                {
779                    throw new AmetysRepositoryException("Cannot get a double from an empty array");
780                }
781            }
782
783            return property.getDouble();
784        }
785        catch (PathNotFoundException e)
786        {
787            if (failIfNotExist)
788            {
789                throw new UnknownMetadataException("Unknown metadata " + METADATA_PREFIX + metadataName + " of " + _node, e);
790            }
791            else
792            {
793                return defaultValue;
794            }
795        }
796        catch (RepositoryException e)
797        {
798            throw new AmetysRepositoryException("Unable to get double property", e);
799        }
800    }
801
802    public double[] getDoubleArray(String metadataName) throws AmetysRepositoryException
803    {
804        return _getDoubleArray(metadataName, null, true);
805    }
806
807    public double[] getDoubleArray(String metadataName, double[] defaultValues)
808    {
809        return _getDoubleArray(metadataName, defaultValues, false);
810    }
811
812    private double[] _getDoubleArray(String metadataName, double[] defaultValues, boolean failIfNotExist)
813    {
814        try
815        {
816            Property property = _node.getProperty(METADATA_PREFIX + metadataName);
817
818            if (property.getDefinition().isMultiple())
819            {
820                Value[] values = property.getValues();
821
822                double[] results = new double[values.length];
823                for (int i = 0; i < values.length; i++)
824                {
825                    Value value = values[i];
826                    results[i] = value.getDouble();
827                }
828
829                return results;
830            }
831
832            Value value = property.getValue();
833            return new double[] {value.getDouble()};
834        }
835        catch (PathNotFoundException e)
836        {
837            if (failIfNotExist)
838            {
839                throw new UnknownMetadataException("Unknown metadata " + METADATA_PREFIX + metadataName + " of " + _node, e);
840            }
841            else
842            {
843                return defaultValues;
844            }
845        }
846        catch (RepositoryException e)
847        {
848            throw new AmetysRepositoryException("Unable to get double array property", e);
849        }
850    }
851    
852    public UserIdentity getUser(String metadataName) throws AmetysRepositoryException
853    {
854        return _getUser(metadataName, null, true);
855    }
856    
857    public UserIdentity getUser(String metadataName, UserIdentity defaultValue) throws AmetysRepositoryException
858    {
859        return _getUser(metadataName, defaultValue, false);
860    }
861    
862    private UserIdentity _getUser(String metadataName, UserIdentity defaultValue, boolean failIfNotExist) throws AmetysRepositoryException
863    {
864        try
865        {
866            Node userNode = _node.getNode(METADATA_PREFIX + metadataName);
867            return new UserIdentity(userNode.getProperty("ametys:login").getString(), userNode.getProperty("ametys:population").getString());
868        }
869        catch (PathNotFoundException e)
870        {
871            if (failIfNotExist)
872            {
873                throw new UnknownMetadataException("Unknown metadata " + METADATA_PREFIX + metadataName + " of " + _node, e);
874            }
875            else
876            {
877                return defaultValue;
878            }
879        }
880        catch (RepositoryException e)
881        {
882            throw new AmetysRepositoryException("Unable to get User metadata", e);
883        }
884    }
885    
886    public UserIdentity[] getUserArray(String metadataName) throws AmetysRepositoryException
887    {
888        return _getUserArray(metadataName, null, true);
889    }
890    
891    public UserIdentity[] getUserArray(String metadataName, UserIdentity[] defaultValues) throws AmetysRepositoryException
892    {
893        return _getUserArray(metadataName, defaultValues, false);
894    }
895    
896    private UserIdentity[] _getUserArray(String metadataName, UserIdentity[] defaultValues, boolean failIfNotExist) throws AmetysRepositoryException
897    {
898        try
899        {
900            if (_node.hasNode(METADATA_PREFIX + metadataName))
901            {
902                List<UserIdentity> results = new ArrayList<>();
903                
904                NodeIterator it = _node.getNodes(METADATA_PREFIX + metadataName);
905                while (it.hasNext())
906                {
907                    Node next = (Node) it.next();
908                    results.add(new UserIdentity(next.getProperty("ametys:login").getString(), next.getProperty("ametys:population").getString()));
909                }
910                
911                return results.toArray(new UserIdentity[results.size()]);
912            }
913            else if (failIfNotExist)
914            {
915                throw new UnknownMetadataException("The metadata '" + metadataName + "' does not exist");
916            }
917            else
918            {
919                return defaultValues;
920            }
921        }
922        catch (RepositoryException e)
923        {
924            throw new AmetysRepositoryException("Unable to get User array metadata", e);
925        }
926    }
927
928    public void setMetadata(String metadataName, String value) throws AmetysRepositoryException
929    {
930        if (value == null)
931        {
932            throw new NullPointerException("metadata value cannot be null");
933        }
934
935        _checkMetadataName(metadataName);
936
937        try
938        {
939            _checkLock();
940            _node.setProperty(METADATA_PREFIX + metadataName, value);
941        }
942        catch (RepositoryException e)
943        {
944            throw new AmetysRepositoryException("Unable to set property", e);
945        }
946    }
947
948    public void setMetadata(String metadataName, String[] value) throws AmetysRepositoryException
949    {
950        if (value == null)
951        {
952            throw new NullPointerException("metadata value cannot be null");
953        }
954
955        _checkMetadataName(metadataName);
956
957        try
958        {
959            _checkLock();
960            _node.setProperty(METADATA_PREFIX + metadataName, value);
961        }
962        catch (RepositoryException e)
963        {
964            throw new AmetysRepositoryException("Unable to set property", e);
965        }
966    }
967
968    public void setMetadata(String metadataName, Date value) throws AmetysRepositoryException
969    {
970        if (value == null)
971        {
972            throw new NullPointerException("metadata value cannot be null");
973        }
974
975        GregorianCalendar calendar = new GregorianCalendar();
976        calendar.setTime(value);
977
978        _checkMetadataName(metadataName);
979
980        try
981        {
982            _checkLock();
983            _node.setProperty(METADATA_PREFIX + metadataName, calendar);
984        }
985        catch (RepositoryException e)
986        {
987            throw new AmetysRepositoryException("Unable to set property", e);
988        }
989    }
990
991    public void setMetadata(String metadataName, Date[] values) throws AmetysRepositoryException
992    {
993        if (values == null)
994        {
995            throw new NullPointerException("metadata value cannot be null");
996        }
997
998        _checkMetadataName(metadataName);
999
1000        try
1001        {
1002            _checkLock();
1003            Value[] jcrValues = new Value[values.length];
1004
1005            for (int i = 0; i < values.length; i++)
1006            {
1007                Date value = values[i];
1008
1009                GregorianCalendar calendar = new GregorianCalendar();
1010                calendar.setTime(value);
1011
1012                jcrValues[i] = _node.getSession().getValueFactory().createValue(calendar);
1013            }
1014
1015            _node.setProperty(METADATA_PREFIX + metadataName, jcrValues);
1016        }
1017        catch (RepositoryException e)
1018        {
1019            throw new AmetysRepositoryException("Unable to set property", e);
1020        }
1021    }
1022
1023    public void setMetadata(String metadataName, long value) throws AmetysRepositoryException
1024    {
1025        _checkMetadataName(metadataName);
1026
1027        try
1028        {
1029            _checkLock();
1030            _node.setProperty(METADATA_PREFIX + metadataName, value);
1031        }
1032        catch (RepositoryException e)
1033        {
1034            throw new AmetysRepositoryException("Unable to set property", e);
1035        }
1036    }
1037
1038    public void setMetadata(String metadataName, double value) throws AmetysRepositoryException
1039    {
1040        _checkMetadataName(metadataName);
1041
1042        try
1043        {
1044            _checkLock();
1045            _node.setProperty(METADATA_PREFIX + metadataName, value);
1046        }
1047        catch (RepositoryException e)
1048        {
1049            throw new AmetysRepositoryException("Unable to set property", e);
1050        }
1051    }
1052
1053    public void setMetadata(String metadataName, long[] values) throws AmetysRepositoryException
1054    {
1055        if (values == null)
1056        {
1057            throw new NullPointerException("metadata value cannot be null");
1058        }
1059
1060        _checkMetadataName(metadataName);
1061
1062        try
1063        {
1064            _checkLock();
1065            Value[] jcrValues = new Value[values.length];
1066
1067            for (int i = 0; i < values.length; i++)
1068            {
1069                long value = values[i];
1070                jcrValues[i] = _node.getSession().getValueFactory().createValue(value);
1071            }
1072
1073            _node.setProperty(METADATA_PREFIX + metadataName, jcrValues);
1074        }
1075        catch (RepositoryException e)
1076        {
1077            throw new AmetysRepositoryException("Unable to set property", e);
1078        }
1079    }
1080
1081    public void setMetadata(String metadataName, double[] values) throws AmetysRepositoryException
1082    {
1083        if (values == null)
1084        {
1085            throw new NullPointerException("metadata value cannot be null");
1086        }
1087
1088        _checkMetadataName(metadataName);
1089
1090        try
1091        {
1092            _checkLock();
1093            Value[] jcrValues = new Value[values.length];
1094
1095            for (int i = 0; i < values.length; i++)
1096            {
1097                double value = values[i];
1098                jcrValues[i] = _node.getSession().getValueFactory().createValue(value);
1099            }
1100
1101            _node.setProperty(METADATA_PREFIX + metadataName, jcrValues);
1102        }
1103        catch (RepositoryException e)
1104        {
1105            throw new AmetysRepositoryException("Unable to set property", e);
1106        }
1107    }
1108
1109    public void setMetadata(String metadataName, boolean value) throws AmetysRepositoryException
1110    {
1111        _checkMetadataName(metadataName);
1112
1113        try
1114        {
1115            _checkLock();
1116            _node.setProperty(METADATA_PREFIX + metadataName, value);
1117        }
1118        catch (RepositoryException e)
1119        {
1120            throw new AmetysRepositoryException("Unable to set property", e);
1121        }
1122    }
1123
1124    public void setMetadata(String metadataName, boolean[] values) throws AmetysRepositoryException
1125    {
1126        if (values == null)
1127        {
1128            throw new NullPointerException("metadata value cannot be null");
1129        }
1130
1131        _checkMetadataName(metadataName);
1132
1133        try
1134        {
1135            _checkLock();
1136            Value[] jcrValues = new Value[values.length];
1137
1138            for (int i = 0; i < values.length; i++)
1139            {
1140                boolean value = values[i];
1141                jcrValues[i] = _node.getSession().getValueFactory().createValue(value);
1142            }
1143
1144            _node.setProperty(METADATA_PREFIX + metadataName, jcrValues);
1145        }
1146        catch (RepositoryException e)
1147        {
1148            throw new AmetysRepositoryException("Unable to set property", e);
1149        }
1150    }
1151    
1152    public void setMetadata(String metadataName, UserIdentity value) throws AmetysRepositoryException
1153    {
1154        _checkMetadataName(metadataName);
1155        
1156        try
1157        {
1158            _checkLock();
1159            
1160            Node userNode = null;
1161            if (_node.hasNode(METADATA_PREFIX + metadataName))
1162            {
1163                userNode = _node.getNode(METADATA_PREFIX + metadataName);
1164            }
1165            else
1166            {
1167                userNode = _node.addNode(METADATA_PREFIX + metadataName, RepositoryConstants.USER_NODETYPE);
1168            }
1169            userNode.setProperty(METADATA_PREFIX + "login", value.getLogin());
1170            userNode.setProperty(METADATA_PREFIX + "population", value.getPopulationId());
1171        }
1172        catch (RepositoryException e)
1173        {
1174            throw new AmetysRepositoryException("Unable to set property", e);
1175        }
1176    }
1177    
1178    public void setMetadata(String metadataName, UserIdentity[] values) throws AmetysRepositoryException
1179    {
1180        if (values == null)
1181        {
1182            throw new NullPointerException("metadata value cannot be null");
1183        }
1184
1185        _checkMetadataName(metadataName);
1186
1187        try
1188        {
1189            _checkLock();
1190            
1191            NodeIterator it = _node.getNodes(METADATA_PREFIX + metadataName);
1192            while (it.hasNext())
1193            {
1194                Node next = (Node) it.next();
1195                next.remove();
1196            }
1197            
1198            for (UserIdentity value : values)
1199            {
1200                Node userNode = _node.addNode(METADATA_PREFIX + metadataName, RepositoryConstants.USER_NODETYPE);
1201                userNode.setProperty(METADATA_PREFIX + "login", value.getLogin());
1202                userNode.setProperty(METADATA_PREFIX + "population", value.getPopulationId());
1203            }
1204        }
1205        catch (RepositoryException e)
1206        {
1207            throw new AmetysRepositoryException("Unable to set property", e);
1208        }
1209    }
1210
1211    public String[] getMetadataNames() throws AmetysRepositoryException
1212    {
1213        try
1214        {
1215            PropertyIterator itProp = _node.getProperties(METADATA_PREFIX + "*");
1216            NodeIterator itNode = _node.getNodes(METADATA_PREFIX + "*");
1217
1218            Set<String> names = new HashSet<>();
1219
1220            while (itProp.hasNext())
1221            {
1222                Property property = itProp.nextProperty();
1223                names.add(property.getName().substring(METADATA_PREFIX.length()));
1224            }
1225
1226            while (itNode.hasNext())
1227            {
1228                Node node = itNode.nextNode();
1229                
1230                // Filtering out AmetysObjects: they're not metadata (except object collections and users).
1231                if (!NodeTypeHelper.isNodeType(node, AmetysObjectResolver.OBJECT_TYPE) || NodeTypeHelper.isNodeType(node, OBJECT_COLLECTION_METADATA_NODETYPE) || NodeTypeHelper.isNodeType(node, RepositoryConstants.USER_NODETYPE))
1232                {
1233                    names.add(node.getName().substring(METADATA_PREFIX.length()));
1234                }
1235            }
1236
1237            String[] result = new String[names.size()];
1238            names.toArray(result);
1239
1240            return result;
1241        }
1242        catch (RepositoryException e)
1243        {
1244            throw new AmetysRepositoryException("Unable to get metadata names", e);
1245        }
1246    }
1247
1248    public boolean isMultiple(String metadataName) throws AmetysRepositoryException
1249    {
1250        try
1251        {
1252            String realMetadataName = METADATA_PREFIX + metadataName;
1253            
1254            if (_node.hasProperty(realMetadataName))
1255            {
1256                Property property = _node.getProperty(realMetadataName);
1257    
1258                if (property.getDefinition().isMultiple())
1259                {
1260                    return true;
1261                }
1262    
1263                return false;
1264            }
1265            
1266            if (_node.hasNode(realMetadataName))
1267            {
1268                String nodeType = NodeTypeHelper.getNodeTypeName(_node.getNode(realMetadataName));
1269                
1270                // Object collection is multi-valued.
1271                if (OBJECT_COLLECTION_METADATA_NODETYPE.equals(nodeType))
1272                {
1273                    return true;
1274                }
1275                
1276                // User metadata
1277                if (nodeType.equals(METADATA_PREFIX + "user"))
1278                {
1279                    return _node.getNodes(realMetadataName).getSize() > 1;
1280                }
1281                    
1282                
1283                // All other complex metadata are single valued.
1284                return false;
1285            }
1286            
1287            throw new UnknownMetadataException("No metadata for name: " + metadataName);
1288        }
1289        catch (RepositoryException e)
1290        {
1291            throw new AmetysRepositoryException("Unable to determine if metadata " + metadataName + " is multiple", e);
1292        }
1293    }
1294
1295    /**
1296     * Check if the metadata name is valid.
1297     * @param metadataName The metadata name to check.
1298     * @throws AmetysRepositoryException If the metadata name is invalid.
1299     */
1300    protected void _checkMetadataName(String metadataName) throws AmetysRepositoryException
1301    {
1302        // Id null
1303        if (metadataName == null)
1304        {
1305            throw new AmetysRepositoryException("Invalid metadata name (null)");
1306        }
1307
1308        // Id valide
1309        if (!_getMetadataNamePattern().matcher(metadataName).matches())
1310        {
1311            throw new AmetysRepositoryException("Invalid metadata name '" + metadataName + "'. This value is not permitted: only [a-zA-Z][a-zA-Z0-9-_]* are allowed.");
1312        }
1313    }
1314
1315    /**
1316     * Get the metadata name pattern to test validity.
1317     * @return The metadata name pattern.
1318     */
1319    protected static Pattern _getMetadataNamePattern()
1320    {
1321        if (__metadataNamePattern == null)
1322        {
1323            // [a-zA-Z][a-zA-Z0-9_]*
1324            __metadataNamePattern = Pattern.compile("[a-z][a-z0-9-_]*", Pattern.CASE_INSENSITIVE);
1325        }
1326
1327        return __metadataNamePattern;
1328    }
1329
1330    public ModifiableBinaryMetadata getBinaryMetadata(String metadataName) throws AmetysRepositoryException
1331    {
1332        return getBinaryMetadata(metadataName, false);
1333    }
1334    
1335    public ModifiableBinaryMetadata getBinaryMetadata(String metadataName, boolean createNew) throws AmetysRepositoryException
1336    {
1337        try
1338        {
1339            if (_node.hasNode(METADATA_PREFIX + metadataName))
1340            {
1341                Node node = _node.getNode(METADATA_PREFIX + metadataName);
1342                return new JCRBinaryMetadata(node);
1343            }
1344            else if (createNew)
1345            {
1346                _checkLock();
1347                Node node = _node.addNode(METADATA_PREFIX + metadataName, METADATA_PREFIX + "binaryMetadata");
1348                return new JCRBinaryMetadata(node);
1349            }
1350            else
1351            {
1352                throw new UnknownMetadataException("No BinaryMetadata named " + metadataName);
1353            }
1354        }
1355        catch (RepositoryException e)
1356        {
1357            throw new AmetysRepositoryException(e);
1358        }
1359    }
1360
1361    public ModifiableCompositeMetadata getCompositeMetadata(String metadataName) throws AmetysRepositoryException
1362    {
1363        return getCompositeMetadata(metadataName, false);
1364    }
1365    
1366    public ModifiableCompositeMetadata getCompositeMetadata(String metadataName, boolean createNew) throws AmetysRepositoryException
1367    {
1368        try
1369        {
1370            if (_node.hasNode(METADATA_PREFIX + metadataName))
1371            {
1372                Node node = _node.getNode(METADATA_PREFIX + metadataName);
1373                return new JCRCompositeMetadata(node, _resolver);
1374            }
1375            else if (createNew)
1376            {
1377                _checkLock();
1378                Node node = _node.addNode(METADATA_PREFIX + metadataName, "ametys:compositeMetadata");
1379                return new JCRCompositeMetadata(node, _resolver);
1380            }
1381            else
1382            {
1383                throw new UnknownMetadataException("No CompositeMetadata named " + metadataName);
1384            }
1385        }
1386        catch (RepositoryException e)
1387        {
1388            throw new AmetysRepositoryException(e);
1389        }
1390    }
1391    
1392    public ModifiableRichText getRichText(String metadataName) throws UnknownMetadataException, AmetysRepositoryException
1393    {
1394        return getRichText(metadataName, false);
1395    }
1396    
1397    public ModifiableRichText getRichText(String metadataName, boolean createNew) throws UnknownMetadataException, AmetysRepositoryException
1398    {
1399        try
1400        {
1401            if (_node.hasNode(METADATA_PREFIX + metadataName))
1402            {
1403                Node node = _node.getNode(METADATA_PREFIX + metadataName);
1404                return new JCRRichText(node, _resolver);
1405            }
1406            else if (createNew)
1407            {
1408                _checkLock();
1409                Node node = _node.addNode(METADATA_PREFIX + metadataName, METADATA_PREFIX + "richText");
1410                return new JCRRichText(node, _resolver);
1411            }
1412            else
1413            {
1414                throw new UnknownMetadataException("No RichText named " + metadataName);
1415            }
1416        }
1417        catch (RepositoryException e)
1418        {
1419            throw new AmetysRepositoryException(e);
1420        }
1421    }
1422    
1423    public ModifiableTraversableAmetysObject getObjectCollection(String metadataName) throws AmetysRepositoryException
1424    {
1425        return getObjectCollection(metadataName, false);
1426    }
1427    
1428    public ModifiableTraversableAmetysObject getObjectCollection(String metadataName, boolean createNew) throws AmetysRepositoryException
1429    {
1430        try
1431        {
1432            if (_node.hasNode(METADATA_PREFIX + metadataName))
1433            {
1434                Node node = _node.getNode(METADATA_PREFIX + metadataName);
1435                return _resolver.resolve(node, false);
1436            }
1437            else if (createNew)
1438            {
1439                _checkLock();
1440                Node node = _node.addNode(METADATA_PREFIX + metadataName, OBJECT_COLLECTION_METADATA_NODETYPE);
1441                return _resolver.resolve(node, false);
1442            }
1443            else
1444            {
1445                throw new UnknownMetadataException("No object collection metadata named " + metadataName);
1446            }
1447        }
1448        catch (RepositoryException e)
1449        {
1450            throw new AmetysRepositoryException(e);
1451        }
1452    }
1453    
1454    @Override
1455    public List<MetadataComment> getComments(String metadataName)
1456    {
1457        List<MetadataComment> comments = new ArrayList<>();
1458        String commentsNodeName = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + metadataName + METADATA_COMMENTS_SUFFIX;
1459        
1460        try
1461        {
1462            if (_node.hasNode(commentsNodeName))
1463            {
1464                Node commentsNode = _node.getNode(commentsNodeName);
1465                NodeIterator iterator = commentsNode.getNodes();
1466                
1467                while (iterator.hasNext())
1468                {
1469                    Node node = iterator.nextNode();
1470                    
1471                    String text = node.getProperty(METADATA_PREFIX + "comment").getString();
1472                    Date date = node.getProperty(METADATA_PREFIX + "date").getDate().getTime();
1473                    String author = node.getProperty(METADATA_PREFIX + "author").getString();
1474                    
1475                    comments.add(new MetadataComment(text, date, author));
1476                }
1477            }
1478            
1479            return comments;
1480        }
1481        catch (RepositoryException e)
1482        {
1483            throw new AmetysRepositoryException("Unable to retrieves the comments for metadata : " + metadataName);
1484        }
1485    }
1486    
1487    @Override
1488    public boolean hasComments(String metadataName)
1489    {
1490        String commentsNodeName = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + metadataName + METADATA_COMMENTS_SUFFIX;
1491        
1492        try
1493        {
1494            if (_node.hasNode(commentsNodeName))
1495            {
1496                Node commentsNode = _node.getNode(commentsNodeName);
1497                return commentsNode.getNodes().hasNext();
1498            }
1499            
1500            return false;
1501        }
1502        catch (RepositoryException e)
1503        {
1504            throw new AmetysRepositoryException("Unable to determine if this node has at least one comment for metadata : " + metadataName, e);
1505        }
1506    }
1507
1508    @Override
1509    public boolean hasComment(String metadataName, int index)
1510    {
1511        String name = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + metadataName + METADATA_COMMENTS_SUFFIX + '/' + RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + index;
1512        try
1513        {
1514            return _node.hasNode(name);
1515        }
1516        catch (RepositoryException e)
1517        {
1518            throw new AmetysRepositoryException("Unable to determine if this node has a comment for metadata : " + metadataName, e);
1519        }
1520    }
1521
1522    @Override
1523    public void addComment(String metadataName, String text, String author, Date date)
1524    {
1525        if (text == null)
1526        {
1527            throw new NullPointerException("text of the comment cannot be null");
1528        }
1529        
1530        String commentsNodeName = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + metadataName + METADATA_COMMENTS_SUFFIX;
1531        
1532        try
1533        {
1534            if (!_node.hasNode(commentsNodeName))
1535            {
1536                _node.addNode(commentsNodeName, "ametys:compositeMetadata");
1537            }
1538            
1539            Node commentsNode = _node.getNode(commentsNodeName);
1540            String nodeName = null;
1541            int i = 0;
1542            
1543            // Loop over existing comments
1544            do
1545            {
1546                nodeName = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + ++i;
1547            } while (commentsNode.hasNode(nodeName));
1548            
1549            // Add the new comment node at the end
1550            _checkLock();
1551            Node commentNode = commentsNode.addNode(nodeName, "ametys:compositeMetadata");
1552            
1553            GregorianCalendar calendar = new GregorianCalendar();
1554            if (date != null)
1555            {
1556                calendar.setTime(date);
1557            }
1558            
1559            commentNode.setProperty(METADATA_PREFIX + "comment", text);
1560            commentNode.setProperty(METADATA_PREFIX + "date", calendar);
1561            commentNode.setProperty(METADATA_PREFIX + "author", author);
1562        }
1563        catch (RepositoryException e)
1564        {
1565            throw new AmetysRepositoryException("Unable to add a comment for metadata : " + metadataName);
1566        }
1567    }
1568
1569    @Override
1570    public void editComment(String metadataName, int index, String text, String author, Date date)
1571    {
1572        if (text == null)
1573        {
1574            throw new NullPointerException("text of the comment cannot be null");
1575        }
1576        
1577        String commentNodeName = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + metadataName + METADATA_COMMENTS_SUFFIX + '/' + RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + index;
1578        
1579        try
1580        {
1581            _checkLock();
1582            Node commentNode = _node.getNode(commentNodeName);
1583            
1584            GregorianCalendar calendar = new GregorianCalendar();
1585            if (date != null)
1586            {
1587                calendar.setTime(date);
1588            }
1589            
1590            commentNode.setProperty(METADATA_PREFIX + "comment", text);
1591            commentNode.setProperty(METADATA_PREFIX + "date", calendar);
1592            commentNode.setProperty(METADATA_PREFIX + "author", author);
1593        }
1594        catch (RepositoryException e)
1595        {
1596            throw new AmetysRepositoryException("Unable to edit the comment at index +" + index + " for metadata : " + metadataName);
1597        }
1598    }
1599
1600    @Override
1601    public void deleteComment(String metadataName, int index)
1602    {
1603        String commentNodeName = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + metadataName + METADATA_COMMENTS_SUFFIX + '/' + RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + index;
1604        
1605        try
1606        {
1607            _checkLock();
1608            _node.getNode(commentNodeName).remove();
1609        }
1610        catch (RepositoryException e)
1611        {
1612            throw new AmetysRepositoryException("Unable to remove the comment at index +" + index + " for metadata : " + metadataName);
1613        }
1614    }
1615    
1616    private void _checkLock() throws RepositoryException
1617    {
1618        if (!_lockAlreadyChecked && _node.isLocked())
1619        {
1620            LockManager lockManager = _node.getSession().getWorkspace().getLockManager();
1621            
1622            Lock lock = lockManager.getLock(_node.getPath());
1623            Node lockHolder = lock.getNode();
1624            
1625            lockManager.addLockToken(lockHolder.getProperty(RepositoryConstants.METADATA_LOCKTOKEN).getString());
1626            _lockAlreadyChecked = true;
1627        }
1628    }
1629}