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