summaryrefslogtreecommitdiffstats
path: root/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/cache/IdentifierCacheKeyFactory.java
blob: 51f47e137b66c84754d2891c197990d250d07219 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/*
 * Copyright (c) 2016 Cisco and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.fd.honeycomb.translate.util.read.cache;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;

import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import javax.annotation.Nonnull;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.Identifiable;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;

/**
 * Factory providing cache keys to easier switching between scopes of caching
 */
public final class IdentifierCacheKeyFactory implements CacheKeyFactory {

    private static final String KEY_PARTS_SEPARATOR = "|";

    // should be Set<Class<? extends DataObject & Identificable<?>>>, but that's not possible for wildcards
    private final Set<Class<? extends DataObject>> additionalKeyTypes;

    /**
     * Construct simple cache key factory
     */
    public IdentifierCacheKeyFactory() {
        this(Collections.emptySet());
    }

    /**
     * @param additionalKeyTypes Additional types from path of cached type, that are specifying scope
     */
    public IdentifierCacheKeyFactory(@Nonnull final Set<Class<? extends DataObject>> additionalKeyTypes) {
        // verify that all are non-null and identifiable
        this.additionalKeyTypes = checkNotNull(additionalKeyTypes, "Additional key types can't be null").stream()
                .map(IdentifierCacheKeyFactory::verifyNotNull)
                .map(IdentifierCacheKeyFactory::verifyIsIdentifiable)
                .collect(Collectors.toSet());
    }

    @Override
    public String createKey(@Nonnull final InstanceIdentifier<?> actualContextIdentifier) {

        checkNotNull(actualContextIdentifier, "Cannot construct key for null InstanceIdentifier");

        // easiest case when only simple key is needed
        if (additionalKeyTypes.isEmpty()) {
            return actualContextIdentifier.getTargetType().toString();
        }

        checkArgument(isUniqueKeyConstructable(actualContextIdentifier),
                "Unable to construct unique key, required key types : %s, provided paths : %s", additionalKeyTypes,
                actualContextIdentifier.getPathArguments());

        return String
                .join(KEY_PARTS_SEPARATOR, additionalKeys(actualContextIdentifier),
                        actualContextIdentifier.getTargetType().toString());
    }

    /**
     * Verifies that all requested key parts have keys
     */
    private boolean isUniqueKeyConstructable(final InstanceIdentifier<?> actualContextIdentifier) {
        return StreamSupport.stream(actualContextIdentifier.getPathArguments().spliterator(), false)
                .filter(this::isAdditionalScope)
                .filter(this::isIdentifiable)
                .count() == additionalKeyTypes.size();
    }

    private boolean isAdditionalScope(final InstanceIdentifier.PathArgument pathArgument) {
        return additionalKeyTypes.contains(pathArgument.getType());
    }

    private boolean isIdentifiable(final InstanceIdentifier.PathArgument pathArgument) {
        return pathArgument instanceof IdentifiableItem;
    }


    private String additionalKeys(final InstanceIdentifier<?> actualContextIdentifier) {
        return StreamSupport.stream(actualContextIdentifier.getPathArguments().spliterator(), false)
                .filter(this::isAdditionalScope)
                .filter(this::isIdentifiable)
                .map(IdentifiableItem.class::cast)
                .map(IdentifierCacheKeyFactory::bindKeyString)
                .collect(Collectors.joining(KEY_PARTS_SEPARATOR));
    }

    private static String bindKeyString(IdentifiableItem identifiableItem) {
        return String.format("%s[%s]", identifiableItem.getType().getTypeName(), identifiableItem.getKey());
    }

    private static Class<? extends DataObject> verifyNotNull(final Class<? extends DataObject> type) {
        return checkNotNull(type, "Cannot use null as key");
    }

    /**
     * Initial check if provided scope variables are identifiable aka. can be used to create unique cache key
     */
    private static Class<? extends DataObject> verifyIsIdentifiable(final Class<? extends DataObject> type) {
        checkArgument(Identifiable.class.isAssignableFrom(type), "Type %s is not Identifiable", type);
        return type;
    }
}