Bug 5540 - Remove ConvertorManager singleton
[openflowplugin.git] / openflowplugin / src / main / java / org / opendaylight / openflowplugin / openflow / md / core / sal / convertor / common / ConvertorProcessor.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.common;
10
11 import java.util.Map;
12 import java.util.Objects;
13 import java.util.Optional;
14 import java.util.concurrent.ConcurrentHashMap;
15 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.ConvertorExecutor;
16 import org.opendaylight.yangtools.yang.binding.DataContainer;
17 import org.slf4j.Logger;
18 import org.slf4j.LoggerFactory;
19
20 /**
21  * Processes source and return result based on convertor cases added to this processor.
22  *
23  * @param <FROM> the source type
24  * @param <TO>   the result type
25  * @param <DATA> the type of convertor data
26  */
27 public class ConvertorProcessor<FROM extends DataContainer, TO, DATA extends ConvertorData> {
28     private static final short OFP_VERSION_ALL = 0x00;
29     private static final Logger LOG = LoggerFactory.getLogger(ConvertorProcessor.class);
30
31     private final Map<Short, Map<Class<?>, ConvertorCase<?, TO, DATA>>> conversions = new ConcurrentHashMap<>();
32     private ConvertorCase<?, TO, DATA> defaultCase;
33
34     /**
35      * Add convertor processor case.
36      *
37      * @param processorCase the processor case
38      * @return the convertor processor
39      */
40     public ConvertorProcessor<FROM, TO, DATA> addCase(final ConvertorCase<?, TO, DATA> processorCase) {
41         if (processorCase.getSupportedVersions().isEmpty()) {
42             getCasesForVersion(OFP_VERSION_ALL).putIfAbsent(processorCase.getType(), processorCase);
43         } else {
44             for (short supportedVersion : processorCase.getSupportedVersions()) {
45                 getCasesForVersion(supportedVersion).putIfAbsent(processorCase.getType(), processorCase);
46             }
47         }
48
49         return this;
50     }
51
52     /**
53      * Process source and return result based on convertor cases, or empty if no match is found.
54      *
55      * @param source the source
56      * @param convertorExecutor convertor executor
57      * @return the optional
58      */
59     public Optional<TO> process(final FROM source, final ConvertorExecutor convertorExecutor) {
60         return process(source, null, convertorExecutor);
61     }
62
63     /**
64      * Process source and return result based on convertor cases, or empty if no match is found.
65      *
66      * @param source the source
67      * @param data   the data
68      * @param convertorExecutor convertor executor
69      * @return the optional
70      */
71     public Optional<TO> process(final FROM source, final DATA data, final ConvertorExecutor convertorExecutor) {
72         Optional<TO> result = Optional.empty();
73         final short version = data != null ? data.getVersion() : OFP_VERSION_ALL;
74
75         if (Objects.isNull(source)) {
76             LOG.trace("Failed to convert null for version {}", version);
77             return result;
78         }
79
80         final Class<?> clazz = source.getImplementedInterface();
81         final Optional<ConvertorCase<?, TO, DATA>> caseOptional = Optional
82                 .ofNullable(getCasesForVersion(version).get(clazz));
83
84         final ConvertorCase<?, TO, DATA> processorCase = caseOptional.orElse(defaultCase);
85
86         if (Objects.nonNull(processorCase)) {
87             result = processorCase.processRaw(source, data, convertorExecutor);
88
89             if (processorCase.isErrorOnEmpty() && !result.isPresent()) {
90                 LOG.warn("Failed to process {} for version {}", clazz, version);
91             }
92         } else {
93             LOG.trace("Failed to process {} for version {}", clazz, version);
94         }
95
96         return result;
97     }
98
99     /**
100      * Sets default case, what will be used when we do not find any matching convertor case for source.
101      *
102      * @param defaultCase the default case
103      * @return the default case
104      */
105     public ConvertorProcessor<FROM, TO, DATA> setDefaultCase(final ConvertorCase<?, TO, DATA> defaultCase) {
106         this.defaultCase = defaultCase;
107         return this;
108     }
109
110     private Map<Class<?>, ConvertorCase<?, TO, DATA>> getCasesForVersion(final short version) {
111         final Map<Class<?>, ConvertorCase<?, TO, DATA>> casesForVersion =
112                 conversions.getOrDefault(version, new ConcurrentHashMap<>());
113
114         conversions.putIfAbsent(version, casesForVersion);
115
116         return casesForVersion;
117     }
118 }