Convert mdsal-binding-dom-codec to a JPMS module
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / ClassGeneratorBridge.java
1 /*
2  * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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 package org.opendaylight.mdsal.binding.dom.codec.impl;
9
10 import static com.google.common.base.Verify.verifyNotNull;
11
12 import com.google.common.annotations.Beta;
13 import com.google.common.base.Supplier;
14 import org.eclipse.jdt.annotation.NonNull;
15 import org.eclipse.jdt.annotation.Nullable;
16 import org.opendaylight.mdsal.binding.dom.codec.impl.loader.CodecClassLoader.ClassGenerator;
17
18 /**
19  * Bridge for initializing generated instance constants during class loading time. This class is public only due to
20  * implementation restrictions and can change at any time.
21  */
22 @Beta
23 public final class ClassGeneratorBridge {
24     interface BridgeProvider<T> extends ClassGenerator<T> {
25         @Override
26         default Class<T> customizeLoading(final @NonNull Supplier<Class<T>> loader) {
27             final BridgeProvider<?> prev = ClassGeneratorBridge.setup(this);
28             try {
29                 final Class<T> result = loader.get();
30
31                 /*
32                  * This a bit of magic to support NodeContextSupplier constants. These constants need to be resolved
33                  * while we have the information needed to find them -- that information is being held in this instance
34                  * and we leak it to a thread-local variable held by CodecDataObjectBridge.
35                  *
36                  * By default the JVM will defer class initialization to first use, which unfortunately is too late for
37                  * us, and hence we need to force class to initialize.
38                  */
39                 try {
40                     Class.forName(result.getName(), true, result.getClassLoader());
41                 } catch (ClassNotFoundException e) {
42                     throw new LinkageError("Failed to find newly-defined " + result, e);
43                 }
44
45                 return result;
46             } finally {
47                 ClassGeneratorBridge.tearDown(prev);
48             }
49         }
50     }
51
52     interface LocalNameProvider<T> extends BridgeProvider<T> {
53
54         @NonNull String resolveLocalName(@NonNull String methodName);
55     }
56
57     interface NodeContextSupplierProvider<T> extends BridgeProvider<T> {
58
59         @NonNull NodeContextSupplier resolveNodeContextSupplier(@NonNull String methodName);
60     }
61
62     private static final ThreadLocal<BridgeProvider<?>> CURRENT_CUSTOMIZER = new ThreadLocal<>();
63
64     private ClassGeneratorBridge() {
65
66     }
67
68     public static @NonNull NodeContextSupplier resolveNodeContextSupplier(final @NonNull String methodName) {
69         return current(NodeContextSupplierProvider.class).resolveNodeContextSupplier(methodName);
70     }
71
72     public static @NonNull String resolveLocalName(final @NonNull String methodName) {
73         return current(LocalNameProvider.class).resolveLocalName(methodName);
74     }
75
76     static @Nullable BridgeProvider<?> setup(final @NonNull BridgeProvider<?> next) {
77         final BridgeProvider<?> prev = CURRENT_CUSTOMIZER.get();
78         CURRENT_CUSTOMIZER.set(verifyNotNull(next));
79         return prev;
80     }
81
82     static void tearDown(final @Nullable BridgeProvider<?> prev) {
83         if (prev == null) {
84             CURRENT_CUSTOMIZER.remove();
85         } else {
86             CURRENT_CUSTOMIZER.set(prev);
87         }
88     }
89
90     private static <T extends BridgeProvider<?>> @NonNull T current(final Class<T> requested) {
91         return requested.cast(verifyNotNull(CURRENT_CUSTOMIZER.get(), "No customizer attached"));
92     }
93 }