001/*
002 * Copyright 2020-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2020-2024 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2020-2024 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.util;
037
038
039
040import java.io.InputStream;
041import java.io.Serializable;
042import java.util.Collections;
043import java.util.Map;
044import java.util.TreeMap;
045import java.util.concurrent.atomic.AtomicReference;
046
047import com.unboundid.ldap.sdk.schema.AttributeSyntaxDefinition;
048import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition;
049import com.unboundid.ldap.sdk.schema.MatchingRuleDefinition;
050import com.unboundid.ldap.sdk.schema.NameFormDefinition;
051import com.unboundid.ldap.sdk.schema.ObjectClassDefinition;
052import com.unboundid.ldap.sdk.schema.Schema;
053import com.unboundid.util.json.JSONObject;
054import com.unboundid.util.json.JSONObjectReader;
055
056
057
058/**
059 * This class represents a data structure with information about a variety of
060 * object identifiers (OIDs) used in LDAP-related contexts.
061 */
062@NotMutable()
063@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
064public final class OIDRegistry
065       implements Serializable
066{
067  /**
068   * A reference to the default instance of this OID registry.
069   */
070  @NotNull private static final AtomicReference<OIDRegistry> DEFAULT_INSTANCE =
071       new AtomicReference<>();
072
073
074
075  /**
076   * The name of the resource that holds the data for the default OID registry.
077   */
078  @NotNull private static final String OID_REGISTRY_JSON_RESOURCE_NAME =
079       "com/unboundid/util/oid-registry.json";
080
081
082
083  /**
084   * The name of the X-ORIGIN extension that schema elements may use to specify
085   * their origin.
086   */
087  @NotNull private static final String X_ORIGIN_EXTENSION_NAME = "X-ORIGIN";
088
089
090
091  /**
092   * The serial version UID for this serializable class.
093   */
094  private static final long serialVersionUID = 867525903925430865L;
095
096
097
098  // A map of the items contained in the OID registry, indexed by
099  @NotNull private final Map<OID,OIDRegistryItem> items;
100
101
102
103  /**
104   * Creates an OID registry instance with the provided set of items.
105   *
106   * @param  items  The map of items to include in the OID registry.
107   */
108  private OIDRegistry(@NotNull final Map<OID,OIDRegistryItem> items)
109  {
110    this.items = items;
111  }
112
113
114
115  /**
116   * Retrieves the default instance of this OID registry.
117   *
118   * @return  The default instance of this OID registry.
119   */
120  @NotNull()
121  public static OIDRegistry getDefault()
122  {
123    OIDRegistry oidRegistry = DEFAULT_INSTANCE.get();
124    if (oidRegistry == null)
125    {
126      synchronized (DEFAULT_INSTANCE)
127      {
128        oidRegistry = DEFAULT_INSTANCE.get();
129        if (oidRegistry == null)
130        {
131          final Map<OID,OIDRegistryItem> items = new TreeMap<>();
132          try (InputStream inputStream =
133                    OIDRegistry.class.getClassLoader().getResourceAsStream(
134                         OID_REGISTRY_JSON_RESOURCE_NAME);
135               JSONObjectReader jsonObjectReader =
136                    new JSONObjectReader(inputStream))
137          {
138            while (true)
139            {
140              final JSONObject o = jsonObjectReader.readObject();
141              if (o == null)
142              {
143                break;
144              }
145
146              try
147              {
148                final OIDRegistryItem item = new OIDRegistryItem(o);
149                items.put(new OID(item.getOID()), item);
150              }
151              catch (final Exception e)
152              {
153                Debug.debugException(e);
154              }
155            }
156          }
157          catch (final Exception e)
158          {
159            Debug.debugException(e);
160          }
161
162          oidRegistry = new OIDRegistry(Collections.unmodifiableMap(items));
163          DEFAULT_INSTANCE.set(oidRegistry);
164        }
165      }
166    }
167
168    return oidRegistry;
169  }
170
171
172
173  /**
174   * Retrieves a copy of this OID registry that has been augmented with
175   * information from the provided schema.
176   *
177   * @param  schema  The schema that may be used to augment the information in
178   *                 this OID registry.  It must not be {@code null}.
179   *
180   * @return  A copy of this OID registry that has been augmented with
181   *          information from the provided schema.
182   */
183  @NotNull()
184  public OIDRegistry withSchema(@NotNull final Schema schema)
185  {
186    final TreeMap<OID,OIDRegistryItem> newItems = new TreeMap<>(items);
187    for (final AttributeSyntaxDefinition syntax : schema.getAttributeSyntaxes())
188    {
189      final String oidString = syntax.getOID();
190      final OID oid = new OID(syntax.getOID());
191      if (newItems.containsKey(oid))
192      {
193        continue;
194      }
195
196      String name = syntax.getDescription();
197      if (name == null)
198      {
199        name = oidString;
200      }
201
202      newItems.put(oid,
203           new OIDRegistryItem(syntax.getOID(), name, "Attribute Syntax",
204                getOrigin(syntax.getExtensions()), null));
205    }
206
207    for (final MatchingRuleDefinition matchingRule : schema.getMatchingRules())
208    {
209      final String oidString = matchingRule.getOID();
210      final OID oid = new OID(matchingRule.getOID());
211      if (newItems.containsKey(oid))
212      {
213        continue;
214      }
215
216      newItems.put(oid,
217           new OIDRegistryItem(matchingRule.getOID(),
218                matchingRule.getNameOrOID(), "Matching Rule",
219                getOrigin(matchingRule.getExtensions()), null));
220    }
221
222    for (final AttributeTypeDefinition attributeType :
223         schema.getAttributeTypes())
224    {
225      final String oidString = attributeType.getOID();
226      final OID oid = new OID(attributeType.getOID());
227      if (newItems.containsKey(oid))
228      {
229        continue;
230      }
231
232      newItems.put(oid,
233           new OIDRegistryItem(attributeType.getOID(),
234                attributeType.getNameOrOID(), "Attribute Type",
235                getOrigin(attributeType.getExtensions()), null));
236    }
237
238    for (final ObjectClassDefinition objectClass :
239         schema.getObjectClasses())
240    {
241      final String oidString = objectClass.getOID();
242      final OID oid = new OID(objectClass.getOID());
243      if (newItems.containsKey(oid))
244      {
245        continue;
246      }
247
248      newItems.put(oid,
249           new OIDRegistryItem(objectClass.getOID(),
250                objectClass.getNameOrOID(), "Object Class",
251                getOrigin(objectClass.getExtensions()), null));
252    }
253
254    for (final NameFormDefinition nameForm : schema.getNameForms())
255    {
256      final String oidString = nameForm.getOID();
257      final OID oid = new OID(nameForm.getOID());
258      if (newItems.containsKey(oid))
259      {
260        continue;
261      }
262
263      newItems.put(oid,
264           new OIDRegistryItem(nameForm.getOID(),
265                nameForm.getNameOrOID(), "Name Form",
266                getOrigin(nameForm.getExtensions()), null));
267    }
268
269    return new OIDRegistry(Collections.unmodifiableMap(newItems));
270  }
271
272
273
274  /**
275   * Retrieves the value for the X-ORIGIN extension from the provided map, if
276   * available.
277   *
278   * @param  extensions  The map of extensions for the associated schema
279   *                     element.
280   *
281   * @return  The value for the X-ORIGIN extension from the provided map, or
282   *          {@code null} if there is no such extension.
283   */
284  @Nullable()
285  private static String getOrigin(
286               @NotNull final Map<String,String[]> extensions)
287  {
288    final String[] values = extensions.get(X_ORIGIN_EXTENSION_NAME);
289    if ((values != null) && (values.length > 0))
290    {
291      return values[0];
292    }
293
294    return null;
295  }
296
297
298
299  /**
300   * Retrieves an unmodifiable map of all items in the OID registry, indexed by
301   * OID.
302   *
303   * @return  An unmodifiable map of all items in the OID registry, indexed by
304   *          OID.
305   */
306  @NotNull()
307  public Map<OID,OIDRegistryItem> getItems()
308  {
309    return items;
310  }
311
312
313
314  /**
315   * Retrieves the OID registry item for the specified OID, if available.
316   *
317   * @param  oid  The OID for the item to retrieve.
318   *
319   * @return  The OID registry item for the specified OID, or {@code null} if
320   *          this registry does not have any information about the specified
321   *          OID.
322   */
323  @Nullable()
324  public OIDRegistryItem get(@NotNull final String oid)
325  {
326    return get(new OID(oid));
327  }
328
329
330
331  /**
332   * Retrieves the OID registry item for the specified OID, if available.
333   *
334   * @param  oid  The OID for the item to retrieve.
335   *
336   * @return  The OID registry item for the specified OID, or {@code null} if
337   *          this registry does not have any information about the specified
338   *          OID.
339   */
340  @Nullable()
341  public OIDRegistryItem get(@NotNull final OID oid)
342  {
343    return items.get(oid);
344  }
345}