bugfix for NonUniqueObjectException when saving term vocabularies
[cdmlib.git] / cdmlib-io / src / main / java / eu / etaxonomy / cdm / io / common / CdmImportBase.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9
10 package eu.etaxonomy.cdm.io.common;
11
12 import java.net.MalformedURLException;
13 import java.net.URI;
14 import java.net.URISyntaxException;
15 import java.sql.ResultSet;
16 import java.sql.SQLException;
17 import java.util.HashSet;
18 import java.util.Set;
19 import java.util.UUID;
20
21 import org.apache.log4j.Logger;
22
23 import eu.etaxonomy.cdm.common.CdmUtils;
24 import eu.etaxonomy.cdm.common.mediaMetaData.ImageMetaData;
25 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
26 import eu.etaxonomy.cdm.io.common.mapping.IInputTransformer;
27 import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
28 import eu.etaxonomy.cdm.model.common.AnnotationType;
29 import eu.etaxonomy.cdm.model.common.CdmBase;
30 import eu.etaxonomy.cdm.model.common.DescriptionElementSource;
31 import eu.etaxonomy.cdm.model.common.ExtensionType;
32 import eu.etaxonomy.cdm.model.common.IOriginalSource;
33 import eu.etaxonomy.cdm.model.common.ISourceable;
34 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
35 import eu.etaxonomy.cdm.model.common.IdentifiableSource;
36 import eu.etaxonomy.cdm.model.common.Language;
37 import eu.etaxonomy.cdm.model.common.MarkerType;
38 import eu.etaxonomy.cdm.model.common.TermVocabulary;
39 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
40 import eu.etaxonomy.cdm.model.description.Feature;
41 import eu.etaxonomy.cdm.model.description.PresenceTerm;
42 import eu.etaxonomy.cdm.model.description.TaxonDescription;
43 import eu.etaxonomy.cdm.model.location.NamedArea;
44 import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
45 import eu.etaxonomy.cdm.model.location.NamedAreaType;
46 import eu.etaxonomy.cdm.model.media.ImageFile;
47 import eu.etaxonomy.cdm.model.media.Media;
48 import eu.etaxonomy.cdm.model.media.MediaRepresentation;
49 import eu.etaxonomy.cdm.model.name.NonViralName;
50 import eu.etaxonomy.cdm.model.name.Rank;
51 import eu.etaxonomy.cdm.model.reference.Reference;
52 import eu.etaxonomy.cdm.model.taxon.Classification;
53 import eu.etaxonomy.cdm.model.taxon.Synonym;
54 import eu.etaxonomy.cdm.model.taxon.Taxon;
55 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
56
57 /**
58 * @author a.mueller
59 * @created 01.07.2008
60 * @version 1.0
61 */
62 public abstract class CdmImportBase<CONFIG extends IImportConfigurator, STATE extends ImportStateBase> extends CdmIoBase<STATE> implements ICdmImport<CONFIG, STATE>{
63 private static Logger logger = Logger.getLogger(CdmImportBase.class);
64
65 protected Classification makeTree(STATE state, Reference reference){
66 Reference ref = CdmBase.deproxy(reference, Reference.class);
67 String treeName = "Classification (Import)";
68 if (ref != null && CdmUtils.isNotEmpty(ref.getTitleCache())){
69 treeName = ref.getTitleCache();
70 }
71 Classification tree = Classification.NewInstance(treeName);
72 tree.setReference(ref);
73
74
75 // use defined uuid for first tree
76 CONFIG config = (CONFIG)state.getConfig();
77 if (state.countTrees() < 1 ){
78 tree.setUuid(config.getClassificationUuid());
79 }
80 getClassificationService().save(tree);
81 state.putTree(ref, tree);
82 return tree;
83 }
84
85
86 /**
87 * Alternative memory saving method variant of
88 * {@link #makeTree(STATE state, Reference ref)} which stores only the
89 * UUID instead of the full tree in the <code>ImportStateBase</code> by
90 * using <code>state.putTreeUuid(ref, tree);</code>
91 *
92 * @param state
93 * @param ref
94 * @return
95 */
96 protected Classification makeTreeMemSave(STATE state, Reference ref){
97 String treeName = "Classification (Import)";
98 if (ref != null && CdmUtils.isNotEmpty(ref.getTitleCache())){
99 treeName = ref.getTitleCache();
100 }
101 Classification tree = Classification.NewInstance(treeName);
102 tree.setReference(ref);
103
104
105 // use defined uuid for first tree
106 CONFIG config = (CONFIG)state.getConfig();
107 if (state.countTrees() < 1 ){
108 tree.setUuid(config.getClassificationUuid());
109 }
110 getClassificationService().save(tree);
111 state.putTreeUuid(ref, tree);
112 return tree;
113 }
114
115
116 protected ExtensionType getExtensionType(STATE state, UUID uuid, String label, String text, String labelAbbrev){
117 ExtensionType extensionType = state.getExtensionType(uuid);
118 if (extensionType == null){
119 extensionType = (ExtensionType)getTermService().find(uuid);
120 if (extensionType == null){
121 extensionType = ExtensionType.NewInstance(text, label, labelAbbrev);
122 extensionType.setUuid(uuid);
123 UUID uuidExtensionTypeVoc = UUID.fromString("117cc307-5bd4-4b10-9b2f-2e14051b3b20");
124 TermVocabulary voc = getVocabularyService().find(uuidExtensionTypeVoc);
125 voc.addTerm(extensionType);
126 getTermService().save(extensionType);
127 }
128 state.putExtensionType(extensionType);
129 }
130 return extensionType;
131 }
132
133
134 protected MarkerType getMarkerType(STATE state, String keyString) {
135 IInputTransformer transformer = state.getTransformer();
136 MarkerType markerType = null;
137 try {
138 markerType = transformer.getMarkerTypeByKey(keyString);
139 } catch (UndefinedTransformerMethodException e) {
140 logger.info("getMarkerTypeByKey not yet implemented for this import");
141 }
142 if (markerType == null ){
143 UUID uuid;
144 try {
145 uuid = transformer.getMarkerTypeUuid(keyString);
146 return getMarkerType(state, uuid, keyString, keyString, keyString);
147 } catch (UndefinedTransformerMethodException e) {
148 logger.warn("getMarkerTypeUuid not yet implemented for this import");
149 }
150 }
151 return null;
152 }
153
154 protected MarkerType getMarkerType(STATE state, UUID uuid, String label, String text, String labelAbbrev){
155 MarkerType markerType = state.getMarkerType(uuid);
156 if (markerType == null){
157 markerType = (MarkerType)getTermService().find(uuid);
158 if (markerType == null){
159 markerType = MarkerType.NewInstance(label, text, labelAbbrev);
160 markerType.setUuid(uuid);
161 UUID uuidMarkerTypeVoc = UUID.fromString("19dffff7-e142-429c-a420-5d28e4ebe305");
162 TermVocabulary voc = getVocabularyService().find(uuidMarkerTypeVoc);
163 voc.addTerm(markerType);
164 getTermService().save(markerType);
165 }
166 state.putMarkerType(markerType);
167 }
168 return markerType;
169 }
170
171 protected AnnotationType getAnnotationType(STATE state, UUID uuid, String label, String text, String labelAbbrev){
172 AnnotationType annotationType = state.getAnnotationType(uuid);
173 if (annotationType == null){
174 annotationType = (AnnotationType)getTermService().find(uuid);
175 if (annotationType == null){
176 annotationType = AnnotationType.NewInstance(label, text, labelAbbrev);
177 annotationType.setUuid(uuid);
178 UUID uuidAnnotationTypeVoc = UUID.fromString("ca04609b-1ba0-4d31-9c2e-aa8eb2f4e62d");
179 TermVocabulary voc = getVocabularyService().find(uuidAnnotationTypeVoc);
180 voc.addTerm(annotationType);
181 getTermService().save(annotationType);
182 }
183 state.putAnnotationType(annotationType);
184 }
185 return annotationType;
186 }
187
188 /**
189 * Returns a named area for a given uuid by first . If the named area does not
190 * @param state
191 * @param uuid
192 * @param label
193 * @param text
194 * @param labelAbbrev
195 * @param areaType
196 * @param level
197 * @return
198 */
199 protected NamedArea getNamedArea(STATE state, UUID uuid, String label, String text, String labelAbbrev, NamedAreaType areaType, NamedAreaLevel level){
200 NamedArea namedArea = state.getNamedArea(uuid);
201 if (namedArea == null){
202 namedArea = (NamedArea)getTermService().find(uuid);
203 if (namedArea == null){
204 namedArea = NamedArea.NewInstance(text, label, labelAbbrev);
205 //FIXME define vocabulary
206 logger.warn("No vocabulary defined for named area");
207 namedArea.setType(areaType);
208 namedArea.setLevel(level);
209 namedArea.setUuid(uuid);
210 getTermService().save(namedArea);
211 }
212 state.putNamedArea(namedArea);
213 }
214 return namedArea;
215 }
216
217 /**
218 * Returns a feature for a given uuid by first ...
219 * @param state
220 * @param uuid
221 * @param label
222 * @param text
223 * @param labelAbbrev
224 * @return
225 */
226 protected Feature getFeature(STATE state, UUID uuid, String label, String text, String labelAbbrev){
227 if (uuid == null){
228 return null;
229 }
230 Feature feature = state.getFeature(uuid);
231 if (feature == null){
232 feature = (Feature)getTermService().find(uuid);
233 if (feature == null){
234 feature = Feature.NewInstance(text, label, labelAbbrev);
235 feature.setUuid(uuid);
236 feature.setSupportsTextData(true);
237 //set vocabulary ; FIXME use another user-defined vocabulary
238 UUID uuidFeatureVoc = UUID.fromString("b187d555-f06f-4d65-9e53-da7c93f8eaa8");
239 TermVocabulary<Feature> voc = getVocabularyService().find(uuidFeatureVoc);
240 voc.addTerm(feature);
241 getTermService().save(feature);
242 }
243 state.putFeature(feature);
244 }
245 return feature;
246 }
247
248 /**
249 * Returns a presence term for a given uuid by first ...
250 * @param state
251 * @param uuid
252 * @param label
253 * @param text
254 * @param labelAbbrev
255 * @return
256 */
257 protected PresenceTerm getPresenceTerm(STATE state, UUID uuid, String label, String text, String labelAbbrev){
258 if (uuid == null){
259 return null;
260 }
261 PresenceTerm presenceTerm = state.getPresenceTerm(uuid);
262 if (presenceTerm == null){
263 presenceTerm = (PresenceTerm)getTermService().find(uuid);
264 if (presenceTerm == null){
265 presenceTerm = PresenceTerm.NewInstance(text, label, labelAbbrev);
266 presenceTerm.setUuid(uuid);
267 //set vocabulary ; FIXME use another user-defined vocabulary
268 UUID uuidPresenceVoc = UUID.fromString("adbbbe15-c4d3-47b7-80a8-c7d104e53a05");
269 TermVocabulary<PresenceTerm> voc = getVocabularyService().find(uuidPresenceVoc);
270 voc.addTerm(presenceTerm);
271 getTermService().save(presenceTerm);
272 }
273 state.putPresenceTerm(presenceTerm);
274 }
275 return presenceTerm;
276 }
277
278 /**
279 * Returns a language for a given uuid by first ...
280 * @param state
281 * @param uuid
282 * @param label
283 * @param text
284 * @param labelAbbrev
285 * @return
286 */
287 protected Language getLanguage(STATE state, UUID uuid, String label, String text, String labelAbbrev){
288 if (uuid == null){
289 return null;
290 }
291 Language language = state.getLanguage(uuid);
292 if (language == null){
293 language = (Language)getTermService().find(uuid);
294 if (language == null){
295 language = Language.NewInstance(text, label, labelAbbrev);
296
297 language.setUuid(uuid);
298 //set vocabulary ; FIXME use another user-defined vocabulary
299 UUID uuidLanguageVoc = UUID.fromString("45ac7043-7f5e-4f37-92f2-3874aaaef2de");
300 TermVocabulary<Language> voc = getVocabularyService().find(uuidLanguageVoc);
301 voc.addTerm(language);
302 getTermService().save(language);
303 }
304 state.putLanguage(language);
305 }
306 return language;
307 }
308
309 /**
310 * Adds an orginal source to a sourceable objects (implemented for Identifiable entity and description element.
311 * If cdmBase is not sourceable nothing happens.
312 * TODO Move to DbImportBase once this exists.
313 * TODO also implemented in DbImportObjectCreationMapper (reduce redundance)
314 * @param rs
315 * @param cdmBase
316 * @param dbIdAttribute
317 * @param namespace
318 * @param citation
319 * @throws SQLException
320 */
321 public void addOriginalSource(CdmBase cdmBase, Object idAttributeValue, String namespace, Reference citation) {
322 if (cdmBase instanceof ISourceable ){
323 IOriginalSource source;
324 ISourceable sourceable = (ISourceable)cdmBase;
325 Object id = idAttributeValue;
326 String strId = String.valueOf(id);
327 String microCitation = null;
328 if (cdmBase instanceof IdentifiableEntity){
329 source = IdentifiableSource.NewInstance(strId, namespace, citation, microCitation);
330 }else if (cdmBase instanceof DescriptionElementBase){
331 source = DescriptionElementSource.NewInstance(strId, namespace, citation, microCitation);
332 }else{
333 logger.warn("ISourceable not beeing identifiable entities or description element base are not yet supported. CdmBase is of type " + cdmBase.getClass().getName() + ". Original source not added.");
334 return;
335 }
336 sourceable.addSource(source);
337 }else if (cdmBase != null){
338 logger.warn("Sourced object does not implement ISourceable: " + cdmBase.getClass() + "," + cdmBase.getUuid());
339 }else{
340 logger.warn("Sourced object is null");
341 }
342 }
343
344 /**
345 * @see #addOriginalSource(CdmBase, Object, String, Reference)
346 * @param rs
347 * @param cdmBase
348 * @param dbIdAttribute
349 * @param namespace
350 * @param citation
351 * @throws SQLException
352 */
353 public void addOriginalSource(ResultSet rs, CdmBase cdmBase, String dbIdAttribute, String namespace, Reference citation) throws SQLException {
354 Object id = rs.getObject(dbIdAttribute);
355 addOriginalSource(cdmBase, id, namespace, citation);
356 }
357
358
359 /**
360 * If the child taxon is missing genus or species epithet information and the rank is below <i>genus</i>
361 * or <i>species</i> respectively the according epithets are taken from the parent taxon.
362 * If the name is an autonym and has no combination author/basionym author the authors are taken from
363 * the parent.
364 * @param parentTaxon
365 * @param childTaxon
366 */
367 protected void fillMissingEpithetsForTaxa(Taxon parentTaxon, Taxon childTaxon) {
368 NonViralName parentName = HibernateProxyHelper.deproxy(parentTaxon.getName(), NonViralName.class);
369 NonViralName childName = HibernateProxyHelper.deproxy(childTaxon.getName(), NonViralName.class);
370 fillMissingEpithets(parentName, childName);
371 }
372
373 /**
374 * If the child name is missing genus or species epithet information and the rank is below <i>genus</i>
375 * or <i>species</i> respectively the according epithets are taken from the parent name.
376 * If the name is an autonym and has no combination author/basionym author the authors are taken from
377 * the parent.
378 * @param parentTaxon
379 * @param childTaxon
380 */
381 protected void fillMissingEpithets(NonViralName parentName, NonViralName childName) {
382 if (CdmUtils.isEmpty(childName.getGenusOrUninomial()) && childName.getRank().isLower(Rank.GENUS()) ){
383 childName.setGenusOrUninomial(parentName.getGenusOrUninomial());
384 }
385
386 if (CdmUtils.isEmpty(childName.getSpecificEpithet()) && childName.getRank().isLower(Rank.SPECIES()) ){
387 childName.setSpecificEpithet(parentName.getSpecificEpithet());
388 }
389 if (childName.isAutonym() && childName.getCombinationAuthorTeam() == null && childName.getBasionymAuthorTeam() == null ){
390 childName.setCombinationAuthorTeam(parentName.getCombinationAuthorTeam());
391 childName.setBasionymAuthorTeam(parentName.getBasionymAuthorTeam());
392 }
393 }
394
395 /**
396 * Returns the image gallery for a taxon. If there are multiple taxon descriptions
397 * marked as image galleries an arbitrary one is chosen.
398 * If no image gallery exists, a new one is created if <code>createNewIfNotExists</code>
399 * is <code>true</code>.
400 * @param createNewIfNotExists
401 * @return
402 */
403 public TaxonDescription getTaxonDescription(Taxon taxon, boolean isImageGallery, boolean createNewIfNotExists) {
404 TaxonDescription result = null;
405 Set<TaxonDescription> descriptions= taxon.getDescriptions();
406 for (TaxonDescription description : descriptions){
407 if (description.isImageGallery() == isImageGallery){
408 result = description;
409 break;
410 }
411 }
412 if (result == null && createNewIfNotExists){
413 result = TaxonDescription.NewInstance(taxon);
414 result.setImageGallery(isImageGallery);
415 }
416 return result;
417 }
418
419
420 /**
421 * Returns the accepted taxon of a {@link TaxonBase taxon base}. <BR>
422 * If taxonBase is of type taxon the same object is returned. If taxonBase is of type
423 * synonym the accepted taxon is returned if one exists. If no accepted taxon exists
424 * <code>null</code> is returned. If multiple accepted taxa exist the one taxon with the
425 * same secundum reference is returned. If no such single taxon exists an
426 * {@link IllegalStateException illegal state exception} is thrown.
427 * @param taxonBase
428 * @return
429 */
430 protected Taxon getAcceptedTaxon(TaxonBase<?> taxonBase) {
431 if (taxonBase == null){
432 return null;
433 }else if(taxonBase.isInstanceOf(Taxon.class)){
434 return CdmBase.deproxy(taxonBase, Taxon.class);
435 }else if(taxonBase.isInstanceOf(Synonym.class)){
436 Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);
437 Set<Taxon> acceptedTaxa = synonym.getAcceptedTaxa();
438 if (acceptedTaxa.size() == 0){
439 return null;
440 }else if (acceptedTaxa.size() == 1){
441 return acceptedTaxa.iterator().next();
442 }else{
443 Reference sec = synonym.getSec();
444 if (sec != null){
445 Set<Taxon> taxaWithSameSec = new HashSet<Taxon>();
446 for (Taxon taxon: acceptedTaxa){
447 if (sec.equals(taxon.getSec())){
448 taxaWithSameSec.add(taxon);
449 }
450 }
451 if (taxaWithSameSec.size() == 1){
452 return taxaWithSameSec.iterator().next();
453 }
454 }
455 throw new IllegalStateException("Can't define the one accepted taxon for a synonym out of multiple accept taxa");
456 }
457 }else{
458 throw new IllegalStateException("Unknown TaxonBase subclass: " + taxonBase.getClass().getName());
459 }
460 }
461
462
463
464 /**
465 * @param derivedUnitFacade
466 * @param multimediaObject
467 * @throws MalformedURLException
468 */
469 protected Media getImageMedia(String multimediaObject, boolean readDataFromUrl) throws MalformedURLException {
470 if( multimediaObject == null){
471 return null;
472 } else {
473 ImageMetaData imd = ImageMetaData.newInstance();
474 URI uri;
475 try {
476 uri = new URI(multimediaObject);
477 try {
478 if (readDataFromUrl){
479 imd.readMetaData(uri, 0);
480 }
481 } catch (Exception e) {
482 String message = "An error occurred when trying to read image meta data: " + e.getMessage();
483 logger.warn(message);
484 }
485 ImageFile imf = ImageFile.NewInstance(uri, null, imd);
486 MediaRepresentation representation = MediaRepresentation.NewInstance();
487 representation.setMimeType(imd.getMimeType());
488 representation.addRepresentationPart(imf);
489 Media media = Media.NewInstance();
490 media.addRepresentation(representation);
491 return media;
492 } catch (URISyntaxException e1) {
493 String message = "An URISyntaxException occurred when trying to create uri from multimedia objcet string: " + multimediaObject;
494 logger.warn(message);
495 return null;
496 }
497 }
498 }
499
500
501 }