Skip to content

Commit b6c2133

Browse files
committed
Add support for extensions in ModelRegistry
Signed-off-by: Gary O'Neall <[email protected]>
1 parent 267a30f commit b6c2133

File tree

4 files changed

+167
-7
lines changed

4 files changed

+167
-7
lines changed

src/main/java/org/spdx/core/ModelRegistry.java

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
*/
66
package org.spdx.core;
77

8+
import java.lang.reflect.Constructor;
9+
import java.lang.reflect.InvocationTargetException;
810
import java.util.ArrayList;
911
import java.util.Collections;
1012
import java.util.HashMap;
@@ -38,6 +40,7 @@ public class ModelRegistry {
3840
private static final ReadWriteLock lock = new ReentrantReadWriteLock();
3941

4042
private final Map<String, ISpdxModelInfo> registeredModels = new HashMap<>();
43+
private final Map<String, Class<? extends CoreModelObject>> extensions = new HashMap<>();
4144

4245
/**
4346
* Private constructor - singleton class
@@ -184,13 +187,43 @@ public CoreModelObject inflateModelObject(IModelStore modelStore, String objectU
184187
if (!containsSpecVersion(specVersion)) {
185188
throw new ModelRegistryException(specVersion + DOES_NOT_EXIST_MSG);
186189
}
187-
return registeredModels.get(specVersion).createModelObject(modelStore, objectUri,
188-
type, copyManager, specVersion, create, idPrefix);
190+
if (extensions.containsKey(type)) {
191+
return inflateExtension(modelStore, objectUri, type, copyManager, specVersion, create, idPrefix);
192+
} else {
193+
return registeredModels.get(specVersion).createModelObject(modelStore, objectUri,
194+
type, copyManager, specVersion, create, idPrefix);
195+
}
189196
} finally {
190197
lock.readLock().unlock();
191198
}
192199
}
193200

201+
private CoreModelObject inflateExtension(IModelStore modelStore, String objectUri, String type,
202+
IModelCopyManager copyManager, String specVersion,
203+
boolean create, String idPrefix) throws InvalidSPDXAnalysisException {
204+
try {
205+
Constructor<?> con = extensions.get(type).getDeclaredConstructor(IModelStore.class, String.class,
206+
IModelCopyManager.class, boolean.class, String.class, String.class);
207+
return (CoreModelObject)con.newInstance(modelStore, objectUri, copyManager, create, specVersion, idPrefix);
208+
} catch (NoSuchMethodException e) {
209+
throw new InvalidSPDXAnalysisException("Could not create the extension type: "+type);
210+
} catch (SecurityException e) {
211+
throw new InvalidSPDXAnalysisException("Unexpected security exception for extension type: "+type, e);
212+
} catch (InstantiationException e) {
213+
throw new InvalidSPDXAnalysisException("Unexpected instantiation exception for extension type: "+type, e);
214+
} catch (IllegalAccessException e) {
215+
throw new InvalidSPDXAnalysisException("Unexpected illegal access exception for extension type: "+type, e);
216+
} catch (IllegalArgumentException e) {
217+
throw new InvalidSPDXAnalysisException("Unexpected illegal argument exception for extension type: "+type, e);
218+
} catch (InvocationTargetException e) {
219+
if (e.getTargetException() instanceof InvalidSPDXAnalysisException) {
220+
throw (InvalidSPDXAnalysisException)e.getTargetException();
221+
} else {
222+
throw new InvalidSPDXAnalysisException("Unexpected invocation target exception for extension type: "+type, e);
223+
}
224+
}
225+
}
226+
194227
/**
195228
* @param type String representation of the SPDX type
196229
* @param specVersion version of the SPDX spec
@@ -205,7 +238,7 @@ public CoreModelObject inflateModelObject(IModelStore modelStore, String objectU
205238
if (!registeredModels.containsKey(specVersion)) {
206239
throw new ModelRegistryException("No implementation found for SPDX spec version "+specVersion);
207240
}
208-
return registeredModels.get(specVersion).getTypeToClassMap().get(type);
241+
return registeredModels.get(specVersion).getTypeToClassMap().getOrDefault(type, extensions.get(type));
209242
} finally {
210243
lock.readLock().unlock();
211244
}
@@ -218,6 +251,7 @@ public void clearAll() {
218251
lock.writeLock().lock();
219252
try {
220253
registeredModels.clear();
254+
extensions.clear();;
221255
} finally {
222256
lock.writeLock().unlock();
223257
}
@@ -252,9 +286,30 @@ public boolean canBeExternal(Class<?> clazz, String specVersion) throws ModelReg
252286
if (!containsSpecVersion(specVersion)) {
253287
throw new ModelRegistryException(specVersion + DOES_NOT_EXIST_MSG);
254288
}
289+
if (extensions.containsValue(clazz)) {
290+
return false;
291+
}
255292
return registeredModels.get(specVersion).canBeExternal(clazz);
256293
} finally {
257294
lock.readLock().unlock();
258295
}
259296
}
297+
298+
/**
299+
* Registers an extension class that can be used to extend an SPDX model
300+
* @param type type to be used
301+
* @param clazz class which must be a subclass of ModelObject
302+
* @return the class which was added to the registry
303+
* @throws ModelRegistryException on missing model registry for the provided specVersion
304+
*/
305+
public Class<?> registerExtensionType(String type, Class<? extends CoreModelObject> clazz) throws ModelRegistryException {
306+
Objects.requireNonNull(clazz, "Class can not be null to register extension type");
307+
Objects.requireNonNull(type, "Type can not be null to register extension type");
308+
lock.writeLock().lock();
309+
try {
310+
return extensions.put(type, clazz);
311+
} finally {
312+
lock.writeLock().unlock();
313+
}
314+
}
260315
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* SPDX-FileCopyrightText: Copyright (c) 2025 Source Auditor Inc.
3+
* SPDX-FileType: SOURCE
4+
* SPDX-License-Identifier: Apache-2.0
5+
* <p>
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
* <p>
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
* <p>
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.spdx.core;
20+
21+
import org.spdx.storage.IModelStore;
22+
23+
public class MockExtension extends MockModelType {
24+
25+
public static final String MOCK_EXTENSION_TYPE = "extensiontype";
26+
27+
public MockExtension(IModelStore modelStore, String objectUri, IModelCopyManager copyManager, boolean create, String specVersion, String idPrefix) throws InvalidSPDXAnalysisException {
28+
super(modelStore, objectUri, copyManager, create, specVersion, idPrefix);
29+
}
30+
31+
@Override
32+
public String getType() {
33+
return MOCK_EXTENSION_TYPE;
34+
}
35+
}

src/test/java/org/spdx/core/MockModelType.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ public MockModelType(IModelStore modelStore, String objectUri,
3636
throws InvalidSPDXAnalysisException {
3737
super(modelStore, objectUri, copyManager, create, specVersion, null);
3838
}
39+
40+
public MockModelType(IModelStore modelStore, String objectUri,
41+
IModelCopyManager copyManager, boolean create, String specVersion, String idPrefix)
42+
throws InvalidSPDXAnalysisException {
43+
super(modelStore, objectUri, copyManager, create, specVersion, idPrefix);
44+
}
3945

4046
public MockModelType(CoreModelObjectBuilder builder, String specVersion) throws InvalidSPDXAnalysisException {
4147
super(builder,specVersion);

src/test/java/org/spdx/core/TestModelRegistry.java

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,7 @@ public class TestModelRegistry {
3131
MockModelStore modelStore;
3232
MockCopyManager copyManager;
3333

34-
/**
35-
* @throws java.lang.Exception
36-
*/
34+
3735
@Before
3836
public void setUp() {
3937
modelStore = new MockModelStore();
@@ -42,7 +40,7 @@ public void setUp() {
4240

4341
/**
4442
* Test method for {@link org.spdx.core.ModelRegistry#containsSpecVersion(java.lang.String)}.
45-
* @throws InvalidSPDXAnalysisException
43+
* @throws InvalidSPDXAnalysisException on error
4644
*/
4745
@Test
4846
public void testAll() throws InvalidSPDXAnalysisException {
@@ -113,4 +111,70 @@ public boolean canBeExternal(Class<?> clazz) {
113111
assertEquals(individual.getIndividualURI(), ((MockIndividual)iResult).getIndividualURI());
114112
}
115113

114+
@Test
115+
public void testExtensions() throws InvalidSPDXAnalysisException {
116+
ModelRegistry.getModelRegistry().clearAll();
117+
assertFalse(ModelRegistry.getModelRegistry().containsSpecVersion("3.0.0"));
118+
Map<String, Enum<?>> uriToEnum = new HashMap<>();
119+
uriToEnum.put(MockEnum.ENUM1.getIndividualURI(), MockEnum.ENUM1);
120+
Map<String, Object> uriToIndividual = new HashMap<>();
121+
MockIndividual individual = new MockIndividual();
122+
uriToIndividual.put(individual.getIndividualURI(), individual);
123+
Map<String, Class<?>> classMap = new HashMap<>();
124+
classMap.put(MockModelType.TYPE, MockModelType.class);
125+
ModelRegistry.getModelRegistry().registerModel(new ISpdxModelInfo() {
126+
127+
@Override
128+
public Map<String, Enum<?>> getUriToEnumMap() {
129+
return uriToEnum;
130+
}
131+
132+
@Override
133+
public List<String> getSpecVersions() {
134+
return Arrays.asList(new String[] {"3.0.0"});
135+
}
136+
137+
@Override
138+
public CoreModelObject createExternalElement(IModelStore store,
139+
String uri, IModelCopyManager copyManager, Class<?> type,
140+
String specVersion) throws InvalidSPDXAnalysisException {
141+
return new MockModelType(store, uri, copyManager, true, specVersion);
142+
}
143+
144+
@Override
145+
public CoreModelObject createModelObject(IModelStore modelStore,
146+
String objectUri, String type,
147+
IModelCopyManager copyManager, String specVersion,
148+
boolean create, String idPrefix) throws InvalidSPDXAnalysisException {
149+
return new MockModelType(modelStore, objectUri, copyManager, create, specVersion);
150+
}
151+
152+
@Override
153+
public Map<String, Class<?>> getTypeToClassMap() {
154+
return classMap;
155+
}
156+
157+
@Override
158+
public Object uriToIndividual(String uri, @Nullable Class<?> type) {
159+
return uriToIndividual.get(uri);
160+
}
161+
162+
@Override
163+
public boolean canBeExternal(Class<?> clazz) {
164+
return false;
165+
}
166+
167+
});
168+
169+
ModelRegistry.getModelRegistry().registerExtensionType(MockExtension.MOCK_EXTENSION_TYPE, MockExtension.class);
170+
assertEquals(MockExtension.class,
171+
ModelRegistry.getModelRegistry().typeToClass(MockExtension.MOCK_EXTENSION_TYPE, "3.0.0"));
172+
CoreModelObject result = ModelRegistry.getModelRegistry().inflateModelObject(modelStore, "https://my.uri",
173+
MockExtension.MOCK_EXTENSION_TYPE, copyManager, "3.0.0", true, "prefix");
174+
assertTrue(result instanceof MockExtension);
175+
assertEquals(MockExtension.MOCK_EXTENSION_TYPE, result.getType());
176+
177+
178+
}
179+
116180
}

0 commit comments

Comments
 (0)