View Javadoc

1   /*
2    * Copyright 2015 Data Archiving and Networked Services (an institute of
3    * Koninklijke Nederlandse Akademie van Wetenschappen), King's College London,
4    * Georg-August-Universitaet Goettingen Stiftung Oeffentlichen Rechts
5    *
6    * Licensed under the EUPL, Version 1.1 or – as soon they will be approved by
7    * the European Commission - subsequent versions of the EUPL (the "Licence");
8    * You may not use this work except in compliance with the Licence.
9    * You may obtain a copy of the Licence at:
10   *
11   * https://joinup.ec.europa.eu/software/page/eupl
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the Licence is distributed on an "AS IS" basis,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the Licence for the specific language governing
17   * permissions and limitations under the Licence.
18   */
19  
20  package eu.ehri.project.models.idgen;
21  
22  import com.google.common.base.Joiner;
23  import com.google.common.collect.ArrayListMultimap;
24  import com.google.common.collect.ListMultimap;
25  import com.google.common.collect.Lists;
26  import eu.ehri.project.acl.SystemScope;
27  import eu.ehri.project.models.base.PermissionScope;
28  import eu.ehri.project.persistence.Bundle;
29  import eu.ehri.project.persistence.Messages;
30  import eu.ehri.project.utils.Slugify;
31  import org.slf4j.Logger;
32  import org.slf4j.LoggerFactory;
33  
34  import java.text.MessageFormat;
35  import java.util.Collection;
36  import java.util.List;
37  
38  /**
39   * Delegation functions for ID generation.
40   */
41  public class IdGeneratorUtils {
42      /**
43       * Separator for ID components.
44       */
45      public static final String HIERARCHY_SEPARATOR = "-";
46      public static final String SLUG_REPLACE = "_";
47  
48      private static final Joiner hierarchyJoiner = Joiner.on(HIERARCHY_SEPARATOR);
49  
50      protected final static Logger logger = LoggerFactory.getLogger(IdGeneratorUtils.class);
51  
52      public static ListMultimap<String, String> handleIdCollision(Collection<String> scopeIds,
53              String dataKey, String ident) {
54  
55          logger.error("ID Generation error: {}={} (scope: {})", dataKey, ident, Lists.newArrayList(scopeIds));
56          ListMultimap<String, String> errors = ArrayListMultimap.create();
57          errors.put(dataKey, MessageFormat.format(
58                  Messages.getString("BundleManager.uniquenessError"), ident));
59          return errors;
60      }
61  
62  
63      /**
64       * Uses the items identifier and its entity type to generate a (supposedly)
65       * unique ID.
66       *
67       * @param scope  the permission scope
68       * @param bundle the item's data bundle
69       * @param ident  the item's identifier
70       */
71      public static String generateId(PermissionScope scope, Bundle bundle, String ident) {
72          List<String> scopeIds = Lists.newArrayList();
73          if (scope != null && !scope.equals(SystemScope.getInstance())) {
74              for (PermissionScope s : scope.getPermissionScopes()) {
75                  scopeIds.add(0, s.getIdentifier());
76              }
77              scopeIds.add(scope.getIdentifier());
78          }
79          return generateId(scopeIds, bundle, ident);
80      }
81  
82      /**
83       * Use an array of scope IDs and the bundle data to generate a unique
84       * id within a given scope.
85       *
86       * @param scopeIds An array of scope ids
87       * @param bundle   The input bundle
88       * @param ident    the item's identifier
89       * @return The complete id string
90       */
91      public static String generateId(Collection<String> scopeIds, Bundle bundle, String ident) {
92  
93          // Validation should have ensured that ident exists...
94          if (ident == null || ident.trim().isEmpty()) {
95              throw new RuntimeException("Invalid null identifier for "
96                      + bundle.getType().getName() + ": " + bundle.getData());
97          }
98          List<String> newIds = Lists.newArrayList(scopeIds);
99          newIds.add(ident);
100         return joinPath(newIds);
101     }
102 
103     /**
104      * Join an identifier path to form a full ID.
105      * Duplicate parts of the part are removed.
106      * This check is case sensitive.
107      *
108      * @param path A non-empty list of identifier strings.
109      * @return The resultant path ID.
110      */
111     public static String joinPath(Collection<String> path) {
112         // If the last part of the path is included in the
113         // current part, remove that chunk, providing that
114         // there is something left over.
115         List<String> newPaths = Lists.newArrayList();
116         String last = null;
117         for (String ident : path) {
118             if (last == null) {
119                 newPaths.add(ident);
120             } else {
121                 if (ident.startsWith(last) && !ident.equals(last)) {
122                     newPaths.add(ident.substring(last.length()));
123                 } else {
124                     newPaths.add(ident);
125                 }
126             }
127             last = ident;
128         }
129 
130         // Slugify the path sections...
131         List<String> slugged = Lists.newArrayList();
132         for (String p : newPaths) {
133             slugged.add(Slugify.slugify(p, SLUG_REPLACE));
134         }
135 
136         return hierarchyJoiner.join(slugged);
137     }
138 }