1 /*
2 * Copyright 2013 The Netty Project
3 *
4 * The Netty Project licenses this file to you under the Apache License,
5 * version 2.0 (the "License"); you may not use this file except in compliance
6 * with the License. You may obtain a copy of the License at:
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations
14 * under the License.
15 */
16
17 package io.netty5.util;
18
19 import java.util.concurrent.ConcurrentHashMap;
20 import java.util.concurrent.ConcurrentMap;
21 import java.util.concurrent.atomic.AtomicInteger;
22
23 import static io.netty5.util.internal.ObjectUtil.checkNonEmpty;
24 import static java.util.Objects.requireNonNull;
25
26 /**
27 * A pool of {@link Constant}s.
28 *
29 * @param <T> the type of the constant
30 */
31 public abstract class ConstantPool<T extends Constant<T>> {
32
33 private final ConcurrentMap<String, T> constants = new ConcurrentHashMap<>();
34
35 private final AtomicInteger nextId = new AtomicInteger(1);
36
37 /**
38 * Shortcut of {@link #valueOf(String) valueOf(firstNameComponent.getName() + "#" + secondNameComponent)}.
39 */
40 public T valueOf(Class<?> firstNameComponent, String secondNameComponent) {
41 return valueOf(
42 requireNonNull(firstNameComponent, "firstNameComponent").getName() + '#' +
43 requireNonNull(secondNameComponent, "secondNameComponent"));
44 }
45
46 /**
47 * Returns the {@link Constant} which is assigned to the specified {@code name}.
48 * If there's no such {@link Constant}, a new one will be created and returned.
49 * Once created, the subsequent calls with the same {@code name} will always return the previously created one
50 * (i.e. singleton.)
51 *
52 * @param name the name of the {@link Constant}
53 */
54 public T valueOf(String name) {
55 return getOrCreate(checkNonEmpty(name, "name"));
56 }
57
58 /**
59 * Get existing constant by name or creates new one if not exists. Threadsafe
60 *
61 * @param name the name of the {@link Constant}
62 */
63 private T getOrCreate(String name) {
64 T constant = constants.get(name);
65 if (constant == null) {
66 final T tempConstant = newConstant(nextId(), name);
67 constant = constants.putIfAbsent(name, tempConstant);
68 if (constant == null) {
69 return tempConstant;
70 }
71 }
72
73 return constant;
74 }
75
76 /**
77 * Returns {@code true} if a {@link AttributeKey} exists for the given {@code name}.
78 */
79 public boolean exists(String name) {
80 return constants.containsKey(checkNonEmpty(name, "name"));
81 }
82
83 /**
84 * Creates a new {@link Constant} for the given {@code name} or fail with an
85 * {@link IllegalArgumentException} if a {@link Constant} for the given {@code name} exists.
86 */
87 public T newInstance(String name) {
88 return createOrThrow(checkNonEmpty(name, "name"));
89 }
90
91 /**
92 * Creates constant by name or throws exception. Threadsafe
93 *
94 * @param name the name of the {@link Constant}
95 */
96 private T createOrThrow(String name) {
97 T constant = constants.get(name);
98 if (constant == null) {
99 final T tempConstant = newConstant(nextId(), name);
100 constant = constants.putIfAbsent(name, tempConstant);
101 if (constant == null) {
102 return tempConstant;
103 }
104 }
105
106 throw new IllegalArgumentException(String.format("'%s' is already in use", name));
107 }
108
109 protected abstract T newConstant(int id, String name);
110
111 @Deprecated
112 public final int nextId() {
113 return nextId.getAndIncrement();
114 }
115 }