001/*
002 * (C) Copyright 2018 Nuxeo (http://nuxeo.com/) and others.
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 *
016 * Contributors:
017 *     pierre
018 */
019package org.nuxeo.runtime.model;
020
021import java.util.ArrayList;
022import java.util.Collection;
023import java.util.Collections;
024import java.util.HashMap;
025import java.util.LinkedHashMap;
026import java.util.List;
027import java.util.Map;
028import java.util.Objects;
029import java.util.stream.Collectors;
030
031import org.apache.logging.log4j.LogManager;
032import org.apache.logging.log4j.Logger;
033
034/**
035 * Default generic descriptor registry.
036 * <p>
037 * It handles (un)registering and merged retrieval.
038 * <p>
039 * Merge algorithm depends on {@code Descriptor} implementations.
040 * <p>
041 *
042 * @since 10.3
043 */
044@SuppressWarnings("unchecked")
045public class DescriptorRegistry {
046
047    private static final Logger log = LogManager.getLogger(DescriptorRegistry.class);
048
049    // target -> xp -> id -> list of descriptors
050    protected Map<String, Map<String, Map<String, List<Descriptor>>>> descriptors = new HashMap<>();
051
052    public <T extends Descriptor> T getDescriptor(String target, String xp, String id) {
053        return (T) merge(descriptors.getOrDefault(target, Collections.emptyMap())
054                                    .getOrDefault(xp, Collections.emptyMap())
055                                    .getOrDefault(id, Collections.emptyList()));
056
057    }
058
059    public <T extends Descriptor> List<T> getDescriptors(String target, String xp) {
060        return (List<T>) descriptors.getOrDefault(target, Collections.emptyMap())
061                                    .getOrDefault(xp, Collections.emptyMap())
062                                    .values()
063                                    .stream()
064                                    .map(this::merge)
065                                    .filter(Objects::nonNull)
066                                    .collect(Collectors.toList());
067    }
068
069    public boolean register(String target, String xp, Descriptor descriptor) {
070        log.debug("Register {} to {}/{}", descriptor.getId(), target, xp);
071        return descriptors.computeIfAbsent(target, k -> new HashMap<>())
072                          .computeIfAbsent(xp, k -> new LinkedHashMap<>())
073                          .computeIfAbsent(descriptor.getId(), k -> new ArrayList<>())
074                          .add(descriptor);
075
076    }
077
078    public boolean unregister(String target, String xp, Descriptor descriptor) {
079        log.debug("Unregister {} from {}/{}", descriptor.getId(), target, xp);
080        return descriptors.getOrDefault(target, Collections.emptyMap())
081                          .getOrDefault(xp, Collections.emptyMap())
082                          .getOrDefault(descriptor.getId(), Collections.emptyList())
083                          .remove(descriptor);
084    }
085
086    protected <T extends Descriptor> T merge(Collection<T> descriptors) {
087        T descriptor = null;
088        for (T d : descriptors) {
089            if (d.doesRemove()) {
090                descriptor = null;
091            } else {
092                descriptor = descriptor == null ? d : (T) descriptor.merge(d);
093            }
094        }
095        return descriptor;
096    }
097
098    public void clear() {
099        descriptors.clear();
100    }
101
102}