|
21 | 21 |
|
22 | 22 | package org.ossreviewtoolkit.clients.fossid |
23 | 23 |
|
24 | | -import com.fasterxml.jackson.core.JsonParser |
25 | | -import com.fasterxml.jackson.core.JsonToken |
26 | | -import com.fasterxml.jackson.databind.BeanProperty |
27 | | -import com.fasterxml.jackson.databind.DeserializationContext |
28 | 24 | import com.fasterxml.jackson.databind.DeserializationFeature |
29 | | -import com.fasterxml.jackson.databind.JavaType |
30 | | -import com.fasterxml.jackson.databind.JsonDeserializer |
31 | 25 | import com.fasterxml.jackson.databind.MapperFeature |
32 | 26 | import com.fasterxml.jackson.databind.ObjectMapper |
33 | 27 | import com.fasterxml.jackson.databind.PropertyNamingStrategies |
34 | | -import com.fasterxml.jackson.databind.deser.ContextualDeserializer |
35 | | -import com.fasterxml.jackson.databind.deser.std.StdDeserializer |
36 | 28 | import com.fasterxml.jackson.module.kotlin.jsonMapper |
37 | 29 | import com.fasterxml.jackson.module.kotlin.kotlinModule |
38 | 30 |
|
@@ -89,143 +81,6 @@ interface FossIdRestService { |
89 | 81 | ) |
90 | 82 | } |
91 | 83 |
|
92 | | - /** |
93 | | - * A class to modify the standard Jackson deserialization to deal with inconsistencies in responses |
94 | | - * sent by the FossID server. |
95 | | - * FossID usually returns data as a List or Map, but in case of no entries it returns a Boolean (which is set to |
96 | | - * false). This custom deserializer streamlines the result: |
97 | | - * - maps are converted to lists by ignoring the keys |
98 | | - * - empty list is returned when the result is Boolean |
99 | | - * - to address a FossID bug in get_all_scans operation, arrays are converted to list. |
100 | | - */ |
101 | | - private class PolymorphicListDeserializer(val boundType: JavaType? = null) : |
102 | | - StdDeserializer<PolymorphicList<Any>>(PolymorphicList::class.java), ContextualDeserializer { |
103 | | - override fun deserialize(p: JsonParser, ctxt: DeserializationContext): PolymorphicList<Any> { |
104 | | - requireNotNull(boundType) { |
105 | | - "The PolymorphicListDeserializer needs a type to deserialize values!" |
106 | | - } |
107 | | - |
108 | | - return when (p.currentToken) { |
109 | | - JsonToken.VALUE_FALSE -> PolymorphicList() |
110 | | - JsonToken.START_ARRAY -> { |
111 | | - val arrayType = ctxt.typeFactory.constructArrayType(boundType) |
112 | | - val array = ctxt.readValue<Array<Any>>(p, arrayType) |
113 | | - PolymorphicList(array.toList()) |
114 | | - } |
115 | | - |
116 | | - JsonToken.START_OBJECT -> { |
117 | | - val mapType = ctxt.typeFactory.constructMapType( |
118 | | - LinkedHashMap::class.java, |
119 | | - String::class.java, |
120 | | - boundType.rawClass |
121 | | - ) |
122 | | - val map = ctxt.readValue<Map<Any, Any>>(p, mapType) |
123 | | - |
124 | | - // Only keep the map's values: If the FossID functions which return a PolymorphicList return a |
125 | | - // map, it always is the list of elements grouped by id. Since the ids are also present in the |
126 | | - // elements themselves, no information is lost by discarding the keys. |
127 | | - PolymorphicList(map.values.toList()) |
128 | | - } |
129 | | - |
130 | | - else -> error("FossID returned a type not handled by this deserializer!") |
131 | | - } |
132 | | - } |
133 | | - |
134 | | - override fun createContextual(ctxt: DeserializationContext?, property: BeanProperty?): JsonDeserializer<*> { |
135 | | - // Extract the type from the property, i.e. the T in PolymorphicList.data<T> |
136 | | - val type = property?.member?.type?.bindings?.getBoundType(0) |
137 | | - return PolymorphicListDeserializer(type) |
138 | | - } |
139 | | - } |
140 | | - |
141 | | - /** |
142 | | - * A custom JSON deserializer implementation to deal with inconsistencies in error responses sent by FossID |
143 | | - * for requests returning a single value. If such a request fails, the response from FossID contains an |
144 | | - * empty array for the value, which cannot be handled by the default deserialization. |
145 | | - */ |
146 | | - private class PolymorphicDataDeserializer(val boundType: JavaType? = null) : |
147 | | - StdDeserializer<PolymorphicData<Any>>(PolymorphicData::class.java), ContextualDeserializer { |
148 | | - override fun deserialize(p: JsonParser, ctxt: DeserializationContext): PolymorphicData<Any> { |
149 | | - requireNotNull(boundType) { |
150 | | - "The PolymorphicDataDeserializer needs a type to deserialize values!" |
151 | | - } |
152 | | - |
153 | | - return when (p.currentToken) { |
154 | | - JsonToken.START_ARRAY -> { |
155 | | - val arrayType = ctxt.typeFactory.constructArrayType(boundType) |
156 | | - val array = ctxt.readValue<Array<Any>>(p, arrayType) |
157 | | - PolymorphicData(array.firstOrNull()) |
158 | | - } |
159 | | - |
160 | | - JsonToken.START_OBJECT -> { |
161 | | - val data = ctxt.readValue<Any>(p, boundType) |
162 | | - PolymorphicData(data) |
163 | | - } |
164 | | - |
165 | | - else -> { |
166 | | - val delegate = ctxt.findNonContextualValueDeserializer(boundType) |
167 | | - PolymorphicData(delegate.deserialize(p, ctxt)) |
168 | | - } |
169 | | - } |
170 | | - } |
171 | | - |
172 | | - override fun createContextual(ctxt: DeserializationContext?, property: BeanProperty?): JsonDeserializer<*> { |
173 | | - val type = property?.member?.type?.bindings?.getBoundType(0) |
174 | | - return PolymorphicDataDeserializer(type) |
175 | | - } |
176 | | - } |
177 | | - |
178 | | - /** |
179 | | - * A class to modify the standard Jackson deserialization to deal with inconsistencies in responses |
180 | | - * sent by the FossID server. |
181 | | - * When deleting a scan, FossID returns the scan id as a string in the `data` property of the response. If no |
182 | | - * scan could be found, it returns an empty array. Starting with FossID version 2023.1, the return type of the |
183 | | - * [deleteScan] function is now a map of strings to strings. Creating a special [FossIdServiceWithVersion] |
184 | | - * implementation for this call is an overkill as ORT does not even use the return value. Therefore, this change |
185 | | - * is also handled by the [PolymorphicIntDeserializer]. |
186 | | - * This custom deserializer streamlines the result: everything is converted to Int and empty array is converted |
187 | | - * to `null`. This deserializer also accepts primitive integers and arrays containing integers and maps of |
188 | | - * strings to strings containing a single entry with an integer value. |
189 | | - */ |
190 | | - private class PolymorphicIntDeserializer : |
191 | | - StdDeserializer<PolymorphicInt>(PolymorphicInt::class.java) { |
192 | | - override fun deserialize(p: JsonParser, ctxt: DeserializationContext): PolymorphicInt { |
193 | | - return when (p.currentToken) { |
194 | | - JsonToken.VALUE_STRING -> { |
195 | | - val value = ctxt.readValue(p, String::class.java) |
196 | | - PolymorphicInt(value.toInt()) |
197 | | - } |
198 | | - |
199 | | - JsonToken.VALUE_NUMBER_INT -> { |
200 | | - val value = ctxt.readValue(p, Int::class.java) |
201 | | - PolymorphicInt(value) |
202 | | - } |
203 | | - |
204 | | - JsonToken.START_ARRAY -> { |
205 | | - val array = ctxt.readValue(p, IntArray::class.java) |
206 | | - val value = if (array.isEmpty()) null else array.first() |
207 | | - PolymorphicInt(value) |
208 | | - } |
209 | | - |
210 | | - JsonToken.START_OBJECT -> { |
211 | | - val mapType = ctxt.typeFactory.constructMapType( |
212 | | - LinkedHashMap::class.java, |
213 | | - String::class.java, |
214 | | - String::class.java |
215 | | - ) |
216 | | - val map = ctxt.readValue<Map<Any, Any>>(p, mapType) |
217 | | - if (map.size != 1) { |
218 | | - error("A map representing a polymorphic integer should have one value!") |
219 | | - } |
220 | | - |
221 | | - PolymorphicInt(map.values.first().toString().toInt()) |
222 | | - } |
223 | | - |
224 | | - else -> error("FossID returned a type not handled by this deserializer!") |
225 | | - } |
226 | | - } |
227 | | - } |
228 | | - |
229 | 84 | /** |
230 | 85 | * Create the [FossIdServiceWithVersion] to interact with the FossID instance running at the given [url], |
231 | 86 | * optionally using a pre-built OkHttp [client]. |
|
0 commit comments