1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package dev.metaschema.core.model.constraint.impl;
7   
8   import java.util.ArrayList;
9   import java.util.List;
10  import java.util.Map;
11  import java.util.concurrent.ConcurrentHashMap;
12  import java.util.stream.Collectors;
13  
14  import dev.metaschema.core.metapath.item.node.INodeItem;
15  import dev.metaschema.core.model.constraint.ConstraintInitializationException;
16  import dev.metaschema.core.model.constraint.IIndex;
17  import dev.metaschema.core.model.constraint.IIndexConstraint;
18  import dev.metaschema.core.model.constraint.IIndexHasKeyConstraint;
19  import dev.metaschema.core.model.constraint.IKeyField;
20  import dev.metaschema.core.model.constraint.IUniqueConstraint;
21  import dev.metaschema.core.util.CollectionUtil;
22  import edu.umd.cs.findbugs.annotations.NonNull;
23  
24  /**
25   * The default implementation of an index that can support the
26   * {@link IIndexConstraint}, {@link IIndexHasKeyConstraint}, and
27   * {@link IUniqueConstraint}.
28   */
29  public class DefaultIndex implements IIndex {
30    @NonNull
31    private final List<IKeyField> keyFields;
32    @NonNull
33    private final Map<List<String>, INodeItem> keyToItemMap = new ConcurrentHashMap<>();
34  
35    /**
36     * Construct a new index.
37     *
38     * @param keyFields
39     *          the key field components to use to generate keys by default
40     */
41    public DefaultIndex(@NonNull List<? extends IKeyField> keyFields) {
42      this.keyFields = CollectionUtil.unmodifiableList(new ArrayList<>(keyFields));
43    }
44  
45    @Override
46    public List<IKeyField> getKeyFields() {
47      return keyFields;
48    }
49  
50    @Override
51    public INodeItem put(@NonNull INodeItem item, @NonNull List<String> key) {
52      INodeItem oldItem = null;
53      if (!IIndex.isAllNulls(key)) {
54        // only add keys with some information (values)
55        oldItem = keyToItemMap.put(key, item);
56      }
57      return oldItem;
58    }
59  
60    @Override
61    public INodeItem get(List<String> key) {
62      int requiredSize = getKeyFields().size();
63      if (requiredSize != key.size()) {
64        throw new ConstraintInitializationException(
65            String.format("Provided key '%s' is not the size '%d' required by the index.",
66                key.stream()
67                    .map(value -> new StringBuilder().append('"').append(value).append('"').toString())
68                    .collect(Collectors.joining(",", "{", "}")),
69                requiredSize));
70      }
71      return keyToItemMap.get(key);
72    }
73  }