1 | |
|
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
|
13 | |
|
14 | |
|
15 | |
|
16 | |
package org.kuali.student.core.document.service.impl; |
17 | |
|
18 | |
import java.util.ArrayList; |
19 | |
import java.util.Arrays; |
20 | |
import java.util.List; |
21 | |
|
22 | |
import javax.jws.WebService; |
23 | |
|
24 | |
import org.kuali.student.common.dictionary.dto.ObjectStructureDefinition; |
25 | |
import org.kuali.student.common.dictionary.service.DictionaryService; |
26 | |
import org.kuali.student.common.dto.StatusInfo; |
27 | |
import org.kuali.student.common.exceptions.DataValidationErrorException; |
28 | |
import org.kuali.student.common.exceptions.DoesNotExistException; |
29 | |
import org.kuali.student.common.exceptions.InvalidParameterException; |
30 | |
import org.kuali.student.common.exceptions.MissingParameterException; |
31 | |
import org.kuali.student.common.exceptions.OperationFailedException; |
32 | |
import org.kuali.student.common.exceptions.PermissionDeniedException; |
33 | |
import org.kuali.student.common.exceptions.VersionMismatchException; |
34 | |
import org.kuali.student.common.search.service.SearchManager; |
35 | |
import org.kuali.student.common.validation.dto.ValidationResultInfo; |
36 | |
import org.kuali.student.common.validator.Validator; |
37 | |
import org.kuali.student.common.validator.ValidatorFactory; |
38 | |
import org.kuali.student.core.document.dao.DocumentDao; |
39 | |
import org.kuali.student.core.document.dto.DocumentCategoryInfo; |
40 | |
import org.kuali.student.core.document.dto.DocumentInfo; |
41 | |
import org.kuali.student.core.document.dto.DocumentTypeInfo; |
42 | |
import org.kuali.student.core.document.dto.RefDocRelationInfo; |
43 | |
import org.kuali.student.core.document.dto.RefDocRelationTypeInfo; |
44 | |
import org.kuali.student.core.document.entity.Document; |
45 | |
import org.kuali.student.core.document.entity.DocumentCategory; |
46 | |
import org.kuali.student.core.document.entity.DocumentType; |
47 | |
import org.kuali.student.core.document.entity.RefDocRelation; |
48 | |
import org.kuali.student.core.document.entity.RefDocRelationType; |
49 | |
import org.kuali.student.core.document.entity.RefObjectSubType; |
50 | |
import org.kuali.student.core.document.entity.RefObjectType; |
51 | |
import org.kuali.student.core.document.service.DocumentService; |
52 | |
import org.springframework.transaction.annotation.Transactional; |
53 | |
|
54 | |
|
55 | |
|
56 | |
|
57 | |
|
58 | |
|
59 | |
|
60 | |
@WebService(endpointInterface = "org.kuali.student.core.document.service.DocumentService", serviceName = "DocumentService", portName = "DocumentService", targetNamespace = "http://student.kuali.org/wsdl/documentService") |
61 | |
@Transactional(readOnly=true,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class}) |
62 | 0 | public class DocumentServiceImpl implements DocumentService { |
63 | |
private DocumentDao dao; |
64 | |
private DictionaryService dictionaryServiceDelegate; |
65 | |
private ValidatorFactory validatorFactory; |
66 | |
private SearchManager searchManager; |
67 | |
|
68 | |
public DocumentDao getDocumentDao(){ |
69 | 0 | return dao; |
70 | |
} |
71 | |
|
72 | |
public void setDocumentDao(DocumentDao dao){ |
73 | 0 | this.dao=dao; |
74 | 0 | } |
75 | |
|
76 | |
|
77 | |
@Override |
78 | |
@Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class}) |
79 | |
public StatusInfo addDocumentCategoryToDocument(String documentId, String documentCategoryKey) throws DataValidationErrorException, DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException, VersionMismatchException { |
80 | 0 | checkForMissingParameter(documentId, "documentId"); |
81 | 0 | checkForMissingParameter(documentCategoryKey, "documentCategoryKey"); |
82 | 0 | StatusInfo statusInfo = new StatusInfo(); |
83 | 0 | statusInfo.setSuccess(dao.addDocumentCategoryToDocument(documentId, documentCategoryKey)); |
84 | |
|
85 | 0 | return statusInfo; |
86 | |
} |
87 | |
|
88 | |
@Override |
89 | |
@Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class}) |
90 | |
public DocumentInfo createDocument(String documentTypeKey, String documentCategoryKey, DocumentInfo documentInfo) throws DataValidationErrorException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException { |
91 | 0 | checkForMissingParameter(documentTypeKey, "documentTypeKey"); |
92 | 0 | checkForMissingParameter(documentCategoryKey, "documentCategoryKey"); |
93 | 0 | checkForMissingParameter(documentInfo, "documentInfo"); |
94 | |
|
95 | |
DocumentType type; |
96 | |
DocumentCategory category; |
97 | |
|
98 | |
|
99 | |
List<ValidationResultInfo> validationResults; |
100 | |
try { |
101 | 0 | validationResults = validateDocument("OBJECT", documentInfo); |
102 | 0 | } catch (DoesNotExistException e) { |
103 | 0 | throw new OperationFailedException("Validation call failed." + e.getMessage()); |
104 | 0 | } |
105 | 0 | if (null != validationResults && validationResults.size() > 0) { |
106 | 0 | throw new DataValidationErrorException("Validation error!", validationResults); |
107 | |
} |
108 | |
|
109 | |
try { |
110 | 0 | type = dao.fetch(DocumentType.class, documentTypeKey); |
111 | 0 | category = dao.fetch(DocumentCategory.class, documentCategoryKey); |
112 | 0 | } catch (DoesNotExistException dnee) { |
113 | 0 | throw new OperationFailedException("error fetching document keys", dnee); |
114 | 0 | } |
115 | |
|
116 | 0 | Document doc = DocumentServiceAssembler.toDocument(new Document(), documentInfo, dao); |
117 | 0 | doc.setType(type); |
118 | 0 | doc.setCategoryList(Arrays.asList(category)); |
119 | 0 | dao.create(doc); |
120 | 0 | return DocumentServiceAssembler.toDocumentInfo(doc); |
121 | |
} |
122 | |
|
123 | |
@Override |
124 | |
@Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class}) |
125 | |
public StatusInfo deleteDocument(String documentId) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException { |
126 | 0 | checkForMissingParameter(documentId, "documentId"); |
127 | 0 | dao.delete(Document.class, documentId); |
128 | 0 | return new StatusInfo(); |
129 | |
} |
130 | |
@Override |
131 | |
public List<DocumentCategoryInfo> getCategoriesByDocument(String documentId) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException { |
132 | 0 | checkForMissingParameter(documentId, "documentId"); |
133 | 0 | List<DocumentCategory> categories = dao.getCategoriesByDocument(documentId); |
134 | 0 | return DocumentServiceAssembler.toDocumentCategoryInfos(categories); |
135 | |
} |
136 | |
@Override |
137 | |
public DocumentInfo getDocument(String documentId) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException { |
138 | 0 | checkForMissingParameter(documentId, "documentId"); |
139 | 0 | return DocumentServiceAssembler.toDocumentInfo(dao.fetch(Document.class, documentId)); |
140 | |
} |
141 | |
@Override |
142 | |
public List<DocumentCategoryInfo> getDocumentCategories() throws OperationFailedException { |
143 | 0 | List<DocumentCategory> categories = dao.find(DocumentCategory.class); |
144 | 0 | return DocumentServiceAssembler.toDocumentCategoryInfos(categories); |
145 | |
} |
146 | |
|
147 | |
@Override |
148 | |
public DocumentCategoryInfo getDocumentCategory(String documentCategoryKey) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException { |
149 | 0 | checkForMissingParameter(documentCategoryKey, "documentCategoryKey"); |
150 | 0 | return DocumentServiceAssembler.toDocumentCategoryInfo(dao.fetch(DocumentCategory.class, documentCategoryKey)); |
151 | |
} |
152 | |
|
153 | |
@Override |
154 | |
public DocumentTypeInfo getDocumentType(String documentTypeKey) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException { |
155 | 0 | checkForMissingParameter(documentTypeKey, "documentTypeKey"); |
156 | 0 | return DocumentServiceAssembler.toGenericTypeInfo(DocumentTypeInfo.class,(dao.fetch(DocumentType.class, documentTypeKey))); |
157 | |
} |
158 | |
|
159 | |
@Override |
160 | |
public List<DocumentTypeInfo> getDocumentTypes() throws OperationFailedException { |
161 | 0 | return DocumentServiceAssembler.toGenericTypeInfoList(DocumentTypeInfo.class,dao.find(DocumentType.class)); |
162 | |
} |
163 | |
|
164 | |
@Override |
165 | |
public List<String> getRefObjectTypes() throws OperationFailedException { |
166 | 0 | return DocumentServiceAssembler.toGenericTypeKeyList(dao.find(RefObjectType.class)); |
167 | |
} |
168 | |
|
169 | |
@Override |
170 | |
public List<String> getRefObjectSubTypes(String refObjectTypeKey) throws MissingParameterException, OperationFailedException { |
171 | 0 | checkForMissingParameter(refObjectTypeKey, "refObjectTypeKey"); |
172 | |
RefObjectType refOjectType; |
173 | |
try { |
174 | 0 | refOjectType = dao.fetch(RefObjectType.class, refObjectTypeKey); |
175 | 0 | } catch (DoesNotExistException e) { |
176 | 0 | return new ArrayList<String>(0); |
177 | 0 | } |
178 | 0 | return DocumentServiceAssembler.toGenericTypeKeyList(refOjectType.getRefObjectSubTypes()); |
179 | |
} |
180 | |
|
181 | |
@Override |
182 | |
public List<RefDocRelationTypeInfo> getRefDocRelationTypes() throws OperationFailedException { |
183 | 0 | return DocumentServiceAssembler.toGenericTypeInfoList(RefDocRelationTypeInfo.class, dao.find(RefDocRelationType.class)); |
184 | |
} |
185 | |
|
186 | |
@Override |
187 | |
public List<RefDocRelationTypeInfo> getRefDocRelationTypesForRefObjectSubType(String refSubTypeKey) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException { |
188 | 0 | checkForMissingParameter(refSubTypeKey, "refSubTypeKey"); |
189 | |
|
190 | 0 | RefObjectSubType refObjectSubType = dao.fetch(RefObjectSubType.class, refSubTypeKey); |
191 | 0 | return DocumentServiceAssembler.toGenericTypeInfoList(RefDocRelationTypeInfo.class, refObjectSubType.getRefDocRelationTypes()); |
192 | |
} |
193 | |
@Override |
194 | |
public List<DocumentInfo> getDocumentsByIdList(List<String> documentIdList) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException { |
195 | 0 | checkForMissingParameter(documentIdList, "documentIdList"); |
196 | 0 | checkForEmptyList(documentIdList, "documentIdList"); |
197 | 0 | List<Document> documents = dao.getDocumentsByIdList(documentIdList); |
198 | 0 | return DocumentServiceAssembler.toDocumentInfos(documents); |
199 | |
} |
200 | |
@Override |
201 | |
public RefDocRelationInfo getRefDocRelation(String refDocRelationId) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException { |
202 | 0 | checkForMissingParameter(refDocRelationId, "refDocRelationId"); |
203 | 0 | return DocumentServiceAssembler.toRefDocRelationInfo(dao.fetch(RefDocRelation.class, refDocRelationId)); |
204 | |
} |
205 | |
@Override |
206 | |
public List<RefDocRelationInfo> getRefDocRelationsByRef(String refObjectTypeKey, String refObjectId) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException { |
207 | 0 | checkForMissingParameter(refObjectTypeKey, "refObjectTypeKey"); |
208 | 0 | checkForMissingParameter(refObjectId, "refObjectId"); |
209 | 0 | return DocumentServiceAssembler.toRefDocRelationInfos(dao.getRefDocRelationsByRef(refObjectTypeKey, refObjectId)); |
210 | |
} |
211 | |
@Override |
212 | |
public List<RefDocRelationInfo> getRefDocRelationsByDoc(String documentId) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException { |
213 | 0 | checkForMissingParameter(documentId, "documentId"); |
214 | 0 | return DocumentServiceAssembler.toRefDocRelationInfos(dao.getRefDocRelationsByDoc(documentId)); |
215 | |
} |
216 | |
|
217 | |
@Override |
218 | |
@Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class}) |
219 | |
public StatusInfo removeDocumentCategoryFromDocument(String documentId, String documentCategoryKey) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException { |
220 | 0 | checkForMissingParameter(documentId, "documentId"); |
221 | 0 | checkForMissingParameter(documentCategoryKey, "documentCategoryKey"); |
222 | 0 | StatusInfo statusInfo = new StatusInfo(); |
223 | 0 | statusInfo.setSuccess(dao.removeDocumentCategoryFromDocument(documentId, documentCategoryKey)); |
224 | 0 | return statusInfo; |
225 | |
} |
226 | |
|
227 | |
|
228 | |
|
229 | |
|
230 | |
@Override |
231 | |
@Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class}) |
232 | |
public DocumentInfo updateDocument(String documentId, DocumentInfo documentInfo) throws DataValidationErrorException, DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException, VersionMismatchException { |
233 | 0 | checkForMissingParameter(documentId, "documentId"); |
234 | 0 | checkForMissingParameter(documentInfo, "documentInfo"); |
235 | |
|
236 | 0 | List<ValidationResultInfo> validationResults = validateDocument("OBJECT", documentInfo); |
237 | 0 | if (null != validationResults && validationResults.size() > 0) { |
238 | 0 | throw new DataValidationErrorException("Validation error!", validationResults); |
239 | |
} |
240 | |
|
241 | 0 | Document document = dao.fetch(Document.class, documentId); |
242 | |
|
243 | 0 | if (!String.valueOf(document.getVersionNumber()).equals(documentInfo.getMetaInfo().getVersionInd())){ |
244 | 0 | throw new VersionMismatchException("Document to be updated is not the current version"); |
245 | |
} |
246 | |
|
247 | 0 | document = DocumentServiceAssembler.toDocument(document, documentInfo, dao); |
248 | |
|
249 | 0 | return DocumentServiceAssembler.toDocumentInfo(dao.update(document)); |
250 | |
} |
251 | |
|
252 | |
|
253 | |
@Override |
254 | |
public List<ValidationResultInfo> validateDocument(String validationType, DocumentInfo documentInfo) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException { |
255 | 0 | checkForMissingParameter(validationType, "validationType"); |
256 | 0 | checkForMissingParameter(documentInfo, "documentInfo"); |
257 | |
|
258 | 0 | ObjectStructureDefinition objStructure = this.getObjectStructure(DocumentInfo.class.getName()); |
259 | 0 | Validator defaultValidator = validatorFactory.getValidator(); |
260 | 0 | List<ValidationResultInfo> validationResults = defaultValidator.validateObject(documentInfo, objStructure); |
261 | 0 | return validationResults; |
262 | |
} |
263 | |
|
264 | |
|
265 | |
@Override |
266 | |
public List<ValidationResultInfo> validateRefDocRelation(String validationType, RefDocRelationInfo refDocRelationInfo) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException { |
267 | 0 | checkForMissingParameter(validationType, "validationType"); |
268 | 0 | checkForMissingParameter(refDocRelationInfo, "refDocRelationInfo"); |
269 | |
|
270 | 0 | ObjectStructureDefinition objStructure = this.getObjectStructure(RefDocRelationInfo.class.getName()); |
271 | 0 | Validator defaultValidator = validatorFactory.getValidator(); |
272 | 0 | List<ValidationResultInfo> validationResults = defaultValidator.validateObject(refDocRelationInfo, objStructure); |
273 | 0 | return validationResults; |
274 | |
} |
275 | |
|
276 | |
@Override |
277 | |
@Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class}) |
278 | |
public RefDocRelationInfo createRefDocRelation(String refObjectTypeKey, String refObjectId, String documentId, String refDocRelationTypeKey, RefDocRelationInfo refDocRelationInfo) throws DataValidationErrorException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException { |
279 | 0 | checkForMissingParameter(refObjectTypeKey, "refObjectTypeKey"); |
280 | 0 | checkForMissingParameter(refObjectId, "refObjectId"); |
281 | 0 | checkForMissingParameter(refDocRelationTypeKey, "refDocRelationTypeKey"); |
282 | 0 | checkForMissingParameter(refDocRelationInfo, "refDocRelationInfo"); |
283 | |
|
284 | |
List<ValidationResultInfo> validationResults; |
285 | |
try { |
286 | 0 | validationResults = validateRefDocRelation("OBJECT", refDocRelationInfo); |
287 | 0 | } catch (DoesNotExistException e) { |
288 | 0 | throw new OperationFailedException("Validation call failed." + e.getMessage()); |
289 | 0 | } |
290 | 0 | if (null != validationResults && validationResults.size() > 0) { |
291 | 0 | throw new DataValidationErrorException("Validation error!", validationResults); |
292 | |
} |
293 | |
|
294 | 0 | refDocRelationInfo.setRefObjectTypeKey(refObjectTypeKey); |
295 | 0 | refDocRelationInfo.setRefObjectId(refObjectId); |
296 | 0 | refDocRelationInfo.setType(refDocRelationTypeKey);; |
297 | |
|
298 | 0 | RefDocRelation refDocRelation = DocumentServiceAssembler.toRefDocRelation(new RefDocRelation(), refDocRelationInfo, dao); |
299 | |
|
300 | 0 | return DocumentServiceAssembler.toRefDocRelationInfo(dao.create(refDocRelation)); |
301 | |
} |
302 | |
|
303 | |
@Override |
304 | |
@Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class}) |
305 | |
public RefDocRelationInfo updateRefDocRelation(String refDocRelationId, RefDocRelationInfo refDocRelationInfo) throws DataValidationErrorException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException, VersionMismatchException, DoesNotExistException { |
306 | 0 | checkForMissingParameter(refDocRelationId, "refDocRelationId"); |
307 | 0 | checkForMissingParameter(refDocRelationInfo, "refDocRelationInfo"); |
308 | |
|
309 | 0 | List<ValidationResultInfo> validationResults = validateRefDocRelation("OBJECT", refDocRelationInfo); |
310 | 0 | if (null != validationResults && validationResults.size() > 0) { |
311 | 0 | throw new DataValidationErrorException("Validation error!", validationResults); |
312 | |
} |
313 | |
|
314 | 0 | refDocRelationInfo.setId(refDocRelationId); |
315 | |
|
316 | 0 | RefDocRelation refDocRelation = dao.fetch(RefDocRelation.class, refDocRelationId); |
317 | |
|
318 | 0 | if (!String.valueOf(refDocRelation.getVersionNumber()).equals(refDocRelationInfo.getMetaInfo().getVersionInd())){ |
319 | 0 | throw new VersionMismatchException("RefDocRelation to be updated is not the current version"); |
320 | |
} |
321 | |
|
322 | 0 | refDocRelation = DocumentServiceAssembler.toRefDocRelation(refDocRelation, refDocRelationInfo, dao); |
323 | |
|
324 | 0 | return DocumentServiceAssembler.toRefDocRelationInfo(dao.update(refDocRelation)); |
325 | |
} |
326 | |
|
327 | |
@Override |
328 | |
@Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class}) |
329 | |
public StatusInfo deleteRefDocRelation(String refDocRelationId) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException { |
330 | 0 | checkForMissingParameter(refDocRelationId, "refDocRelationId"); |
331 | 0 | dao.delete(RefDocRelation.class, refDocRelationId); |
332 | 0 | return new StatusInfo(); |
333 | |
} |
334 | |
|
335 | |
|
336 | |
|
337 | |
|
338 | |
|
339 | |
|
340 | |
|
341 | |
|
342 | |
|
343 | |
private void checkForMissingParameter(Object param, String paramName) |
344 | |
throws MissingParameterException { |
345 | 0 | if (param == null) { |
346 | 0 | throw new MissingParameterException(paramName + " can not be null"); |
347 | |
} |
348 | 0 | } |
349 | |
|
350 | |
|
351 | |
|
352 | |
|
353 | |
|
354 | |
|
355 | |
private void checkForEmptyList(Object param, String paramName) |
356 | |
throws MissingParameterException { |
357 | 0 | if (param != null && param instanceof List<?> && ((List<?>)param).size() == 0) { |
358 | 0 | throw new MissingParameterException(paramName + " can not be an empty list"); |
359 | |
} |
360 | 0 | } |
361 | |
|
362 | |
@Override |
363 | |
public ObjectStructureDefinition getObjectStructure(String objectTypeKey) { |
364 | 0 | return dictionaryServiceDelegate.getObjectStructure(objectTypeKey); |
365 | |
} |
366 | |
|
367 | |
@Override |
368 | |
public List<String> getObjectTypes() { |
369 | 0 | return dictionaryServiceDelegate.getObjectTypes(); |
370 | |
} |
371 | |
|
372 | |
public DictionaryService getDictionaryServiceDelegate() { |
373 | 0 | return dictionaryServiceDelegate; |
374 | |
} |
375 | |
|
376 | |
public void setDictionaryServiceDelegate(DictionaryService dictionaryServiceDelegate) { |
377 | 0 | this.dictionaryServiceDelegate = dictionaryServiceDelegate; |
378 | 0 | } |
379 | |
|
380 | |
public DocumentDao getDao() { |
381 | 0 | return dao; |
382 | |
} |
383 | |
|
384 | |
public void setDao(DocumentDao dao) { |
385 | 0 | this.dao = dao; |
386 | 0 | } |
387 | |
|
388 | |
public SearchManager getSearchManager() { |
389 | 0 | return searchManager; |
390 | |
} |
391 | |
|
392 | |
public void setSearchManager(SearchManager searchManager) { |
393 | 0 | this.searchManager = searchManager; |
394 | 0 | } |
395 | |
|
396 | |
public ValidatorFactory getValidatorFactory() { |
397 | 0 | return validatorFactory; |
398 | |
} |
399 | |
|
400 | |
public void setValidatorFactory(ValidatorFactory validatorFactory) { |
401 | 0 | this.validatorFactory = validatorFactory; |
402 | 0 | } |
403 | |
} |