merge 3.0.2 to trunk
[cdmlib.git] / cdmlib-io / src / main / java / eu / etaxonomy / cdm / io / tcsxml / in / TcsXmlTaxonNameImport.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.tcsxml.in;
11
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.List;
15
16 import org.apache.commons.lang.StringUtils;
17 import org.apache.log4j.Logger;
18 import org.jdom.Element;
19 import org.jdom.Namespace;
20 import org.springframework.stereotype.Component;
21
22 import eu.etaxonomy.cdm.common.ResultWrapper;
23 import eu.etaxonomy.cdm.common.XmlHelp;
24 import eu.etaxonomy.cdm.io.common.ICdmIO;
25 import eu.etaxonomy.cdm.io.common.ImportHelper;
26 import eu.etaxonomy.cdm.io.common.MapWrapper;
27 import eu.etaxonomy.cdm.io.tcsxml.TcsXmlTransformer;
28 import eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor;
29 import eu.etaxonomy.cdm.model.agent.Person;
30 import eu.etaxonomy.cdm.model.agent.Team;
31 import eu.etaxonomy.cdm.model.name.CultivarPlantName;
32 import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
33 import eu.etaxonomy.cdm.model.name.NonViralName;
34 import eu.etaxonomy.cdm.model.name.Rank;
35 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
36 import eu.etaxonomy.cdm.model.name.ZoologicalName;
37 import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
38 import eu.etaxonomy.cdm.model.reference.Reference;
39 import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
40
41 @Component("tcsXmlTaxonNameIO")
42 public class TcsXmlTaxonNameImport extends TcsXmlImportBase implements ICdmIO<TcsXmlImportState> {
43 private static final Logger logger = Logger.getLogger(TcsXmlTaxonNameImport.class);
44
45 private static int modCount = 5000;
46
47 public TcsXmlTaxonNameImport(){
48 super();
49 }
50
51 @Override
52 public boolean doCheck(TcsXmlImportState state){
53 boolean result = true;
54 logger.warn("BasionymRelations not yet implemented");
55 logger.warn("Checking for TaxonNames not yet implemented");
56 //result &= checkArticlesWithoutJournal(tcsConfig);
57 //result &= checkPartOfJournal(tcsConfig);
58
59 return result;
60 }
61
62 //@SuppressWarnings("unchecked")
63 @Override
64 public boolean doInvoke(TcsXmlImportState state){
65
66 logger.info("start make TaxonNames ...");
67 MapWrapper<Person> authorMap = (MapWrapper<Person>)state.getStore(ICdmIO.TEAM_STORE);
68 MapWrapper<TaxonNameBase> taxonNameMap = (MapWrapper<TaxonNameBase>)state.getStore(ICdmIO.TAXONNAME_STORE);
69 MapWrapper<Reference> referenceMap = (MapWrapper<Reference>)state.getStore(ICdmIO.REFERENCE_STORE);
70
71 ResultWrapper<Boolean> success = ResultWrapper.NewInstance(true);
72 String childName;
73 boolean obligatory;
74 String idNamespace = "TaxonName";
75
76 TcsXmlImportConfigurator config = state.getConfig();
77 Element elDataSet = getDataSetElement(config);
78 Namespace tcsNamespace = config.getTcsXmlNamespace();
79
80 childName = "TaxonNames";
81 obligatory = false;
82 Element elTaxonNames = XmlHelp.getSingleChildElement(success, elDataSet, childName, tcsNamespace, obligatory);
83
84 String tcsElementName = "TaxonName";
85 List<Element> elTaxonNameList = (List<Element>)elTaxonNames.getChildren(tcsElementName, tcsNamespace);
86
87 int i = 0;
88 //for each taxonName
89 for (Element elTaxonName : elTaxonNameList){
90 if ((++i % modCount) == 0){ logger.info("Names handled: " + (i-1));}
91 List<String> elementList = new ArrayList<String>();
92
93 //create TaxonName element
94 String strId = elTaxonName.getAttributeValue("id");
95 String strNomenclaturalCode = elTaxonName.getAttributeValue("nomenclaturalCode");
96
97 childName = "Rank";
98 obligatory = false;
99 Element elRank = XmlHelp.getSingleChildElement(success, elTaxonName, childName, tcsNamespace, obligatory);
100 Rank rank = makeRank(elRank);
101 elementList.add(childName.toString());
102
103
104 try {
105 TaxonNameBase<?,?> nameBase;
106 NomenclaturalCode nomCode = TcsXmlTransformer.nomCodeString2NomCode(strNomenclaturalCode);
107 if (nomCode != null){
108 nameBase = nomCode.getNewTaxonNameInstance(rank);
109 }else{
110 nameBase = NonViralName.NewInstance(rank);
111 }
112 childName = "Simple";
113 obligatory = true;
114 Element elSimple = XmlHelp.getSingleChildElement(success, elTaxonName, childName, tcsNamespace, obligatory);
115 String simple = (elSimple == null)? "" : elSimple.getTextNormalize();
116 nameBase.setTitleCache(simple, false);
117 elementList.add(childName.toString());
118
119 childName = "CanonicalName";
120 obligatory = false;
121 Element elCanonicalName = XmlHelp.getSingleChildElement(success, elTaxonName, childName, tcsNamespace, obligatory);
122 makeCanonicalName(nameBase, elCanonicalName, taxonNameMap, success);
123 elementList.add(childName.toString());
124
125 childName = "CanonicalAuthorship";
126 obligatory = false;
127 Element elCanonicalAuthorship = XmlHelp.getSingleChildElement(success, elTaxonName, childName, tcsNamespace, obligatory);
128 makeCanonicalAuthorship(nameBase, elCanonicalAuthorship, authorMap, success);
129 elementList.add(childName.toString());
130
131 childName = "PublishedIn";
132 obligatory = false;
133 Element elPublishedIn = XmlHelp.getSingleChildElement(success, elTaxonName, childName, tcsNamespace, obligatory);
134 makePublishedIn(nameBase, elPublishedIn, referenceMap, success);
135 elementList.add(childName.toString());
136
137 childName = "Year";
138 obligatory = false;
139 Element elYear = XmlHelp.getSingleChildElement(success, elTaxonName, childName, tcsNamespace, obligatory);
140 makeYear(nameBase, elYear, success);
141 elementList.add(childName.toString());
142
143 childName = "MicroReference";
144 obligatory = false;
145 Element elMicroReference = XmlHelp.getSingleChildElement(success, elTaxonName, childName, tcsNamespace, obligatory);
146 makeMicroReference(nameBase, elMicroReference, success);
147 elementList.add(childName.toString());
148
149 childName = "Typification";
150 obligatory = false;
151 Element elTypification = XmlHelp.getSingleChildElement(success, elTaxonName, childName, tcsNamespace, obligatory);
152 makeTypification(nameBase, elTypification, success);
153 elementList.add(childName.toString());
154
155 childName = "PublicationStatus";
156 obligatory = false;
157 Element elPublicationStatus = XmlHelp.getSingleChildElement(success, elTaxonName, childName, tcsNamespace, obligatory);
158 makePublicationStatus(nameBase, elPublicationStatus, success);
159 elementList.add(childName.toString());
160
161 childName = "ProviderLink";
162 obligatory = false;
163 Element elProviderLink = XmlHelp.getSingleChildElement(success, elTaxonName, childName, tcsNamespace, obligatory);
164 makeProviderLink(nameBase, elProviderLink, success);
165 elementList.add(childName.toString());
166
167 childName = "ProviderSpecificData";
168 obligatory = false;
169 Element elProviderSpecificData = XmlHelp.getSingleChildElement(success, elTaxonName, childName, tcsNamespace, obligatory);
170 makeProviderSpecificData(nameBase, elProviderSpecificData, success);
171 elementList.add(childName.toString());
172
173
174 ImportHelper.setOriginalSource(nameBase, config.getSourceReference(), strId, idNamespace);
175
176 taxonNameMap.put(strId, nameBase);
177
178 } catch (UnknownCdmTypeException e) {
179 logger.warn("Name with id " + strId + " has unknown nomenclatural code.");
180 success.setValue(false);
181 }
182 }
183 logger.info(i + " names handled");
184 Collection<? extends TaxonNameBase> col = taxonNameMap.objects();
185 getNameService().save((Collection)col);
186
187 logger.info("end makeTaxonNames ...");
188 return success.getValue();
189
190 }
191
192 /**
193 * Returns the rank represented by the rank element.<br>
194 * Returns <code>null</code> if the element is null.<br>
195 * Returns <code>null</code> if the code and the text are both either empty or do not exists.<br>
196 * Returns the rank represented by the code attribute, if the code attribute is not empty and could be resolved.<br>
197 * If the code could not be resolved it returns the rank represented most likely by the elements text.<br>
198 * Returns UNKNOWN_RANK if code attribute and element text could not be resolved.
199 * @param elRank tcs rank element
200 * @return
201 */
202 protected static Rank makeRank(Element elRank){
203 Rank result;
204 if (elRank == null){
205 return null;
206 }
207 String strRankCode = elRank.getAttributeValue("code");
208 String strRankString = elRank.getTextNormalize();
209 if ( StringUtils.isBlank(strRankCode) && StringUtils.isBlank(strRankString)){
210 return null;
211 }
212
213 Rank codeRank = null;
214 try {
215 codeRank = TcsXmlTransformer.rankCode2Rank(strRankCode);
216 } catch (UnknownCdmTypeException e1) {
217 codeRank = Rank.UNKNOWN_RANK();
218 }
219 Rank stringRank = null;
220 try {
221 boolean useUnknown = true;
222 stringRank = Rank.getRankByNameOrAbbreviation(strRankString, useUnknown);
223 } catch (UnknownCdmTypeException e1) {
224 //does not happen because of useUnknown = true
225 }
226
227 //codeRank exists
228 if ( (codeRank != null) && ! codeRank.equals(Rank.UNKNOWN_RANK())){
229 result = codeRank;
230 if (! codeRank.equals(stringRank) && ! stringRank.equals(Rank.UNKNOWN_RANK())){
231 logger.warn("code rank and string rank are unequal. code: " + codeRank.getLabel() + " <-> string: " + stringRank.getLabel());
232 }
233 }
234 //codeRank does not exist
235 else{
236 result = stringRank;
237 logger.warn("string rank used, because code rank does not exist or was not recognized: " + stringRank.getLabel());
238 }
239 return result;
240 }
241
242 private void makeCanonicalName(TaxonNameBase name, Element elCanonicalName, MapWrapper<TaxonNameBase> taxonNameMap, ResultWrapper<Boolean> success){
243 boolean cacheProtected = false;
244
245 if (elCanonicalName == null){
246 return;
247 }
248 Namespace ns = elCanonicalName.getNamespace();
249
250 String childName = "Simple";
251 boolean obligatory = true;
252 Element elSimple = XmlHelp.getSingleChildElement(success, elCanonicalName, childName, ns, obligatory);
253 String simple = (elSimple == null)? "" : elSimple.getTextNormalize();
254 name.setFullTitleCache(simple, cacheProtected);
255
256 if (name instanceof NonViralName<?>){
257 NonViralName<?> nonViralName = (NonViralName<?>)name;
258 childName = "Uninomial";
259 obligatory = false;
260 Element elUninomial = XmlHelp.getSingleChildElement(success, elCanonicalName, childName, ns, obligatory);
261 String uninomial = (elUninomial == null)? "" : elUninomial.getTextNormalize();
262 if (StringUtils.isNotBlank(uninomial)){
263 nonViralName.setGenusOrUninomial(uninomial);
264 if (nonViralName.getRank() != null && nonViralName.getRank().isLower(Rank.GENUS())){ // TODO check
265 logger.warn("Name " + simple + " lower then 'genus' but has a canonical name part 'Uninomial'.");
266 }
267 }
268 testNoMoreElements();
269
270 childName = "Genus";
271 obligatory = false;
272 Element elGenus = XmlHelp.getSingleChildElement(success, elCanonicalName, childName, ns, obligatory);
273 if (elGenus != null){
274 //TODO do Attributes reference
275 makeGenusReferenceType(name, elGenus, taxonNameMap, success);
276 String genus = elGenus.getTextNormalize();
277 if (StringUtils.isNotBlank(genus)){
278 nonViralName.setGenusOrUninomial(genus);
279 if (nonViralName.getRank() != null && ! nonViralName.getRank().isLower(Rank.GENUS() )){ // TODO check
280 logger.warn("Name " + simple + " is not lower then 'genus' but has canonical name part 'Genus'.");
281 }
282 }
283 }
284
285 childName = "InfragenericEpithet";
286 obligatory = false;
287 Element elInfrageneric = XmlHelp.getSingleChildElement(success, elCanonicalName, childName, ns, obligatory);
288 String infraGenericEpithet = (elInfrageneric == null)? "" : elInfrageneric.getTextNormalize();
289 if (! infraGenericEpithet.trim().equals("")){
290 nonViralName.setInfraGenericEpithet(infraGenericEpithet);
291 if (nonViralName.getRank() != null && ! name.getRank().isInfraGeneric()){
292 logger.warn("Name " + simple + " is not infra generic but has canonical name part 'InfragenericEpithet'.");
293 }
294 }
295 testNoMoreElements();
296
297 childName = "SpecificEpithet";
298 obligatory = false;
299 Element elSpecificEpithet = XmlHelp.getSingleChildElement(success, elCanonicalName, childName, ns, obligatory);
300 String specificEpithet = (elSpecificEpithet == null)? "" : elSpecificEpithet.getTextNormalize();
301 if (! specificEpithet.trim().equals("")){
302 nonViralName.setSpecificEpithet(specificEpithet);
303 if (nonViralName.getRank() != null && name.getRank().isHigher(Rank.SPECIES()) ){
304 logger.warn("Name " + simple + " is not species or below but has canonical name part 'SpecificEpithet'.");
305 }
306 }
307 testNoMoreElements();
308
309 childName = "InfraspecificEpithet";
310 obligatory = false;
311 Element elInfraspecificEpithet = XmlHelp.getSingleChildElement(success, elCanonicalName, childName, ns, obligatory);
312 String infraspecificEpithet = (elInfraspecificEpithet == null)? "" : elInfraspecificEpithet.getTextNormalize();
313 if (! infraspecificEpithet.trim().equals("")){
314 nonViralName.setInfraSpecificEpithet(infraspecificEpithet);
315 if (nonViralName.getRank() != null && ! name.isInfraSpecific() ){
316 logger.warn("Name " + simple + " is not infraspecific but has canonical name part 'InfraspecificEpithet'.");
317 }
318 }
319 testNoMoreElements();
320
321
322 }else{ //ViralName
323 //logger.warn("Non NonViralNames not yet supported by makeCanonicalName");
324 }
325
326
327 childName = "CultivarNameGroup";
328 obligatory = false;
329 Element elCultivarNameGroup = XmlHelp.getSingleChildElement(success, elCanonicalName, childName, ns, obligatory);
330 String cultivarNameGroup = (elCultivarNameGroup == null)? "" : elCultivarNameGroup.getTextNormalize();
331 if (! "".equals(cultivarNameGroup.trim())){
332 if (name instanceof CultivarPlantName){
333 makeCultivarName();
334 }else{
335 logger.warn("Non cultivar name has 'cultivar name group' element. Omitted");
336 }
337 }
338 return;
339 }
340
341 protected void testNoMoreElements(){
342 //TODO
343 //logger.info("testNoMoreElements Not yet implemented");
344 }
345
346 private void makeCultivarName(){
347 //TODO
348 //logger.warn("'makeCultivarName' Not yet implemented");
349 }
350
351 private void makeGenusReferenceType(TaxonNameBase name, Element elGenus, MapWrapper<TaxonNameBase> taxonNameMap, ResultWrapper<Boolean> success){
352 if(name instanceof NonViralName){
353 NonViralName nonViralName = (NonViralName)name;
354 if (elGenus != null && name != null){
355 TaxonNameBase genusReferenceName;
356 //TODO code
357 Class<? extends NonViralName> clazz = NonViralName.class;
358 genusReferenceName = makeReferenceType(elGenus, clazz, taxonNameMap, success);
359 NonViralName nvGenusReference = (NonViralName)genusReferenceName;
360 //Genus is stored either in Genus part (if ref) or in titleCache (if plain text)
361 String genus = nvGenusReference.getGenusOrUninomial()!= null ? nvGenusReference.getGenusOrUninomial(): genusReferenceName.getTitleCache();
362 nonViralName.setGenusOrUninomial(genus);
363 }else{
364 logger.warn("Missing Genus information");
365 }
366 }else{
367 //TODO (can be changed if Viral Name also has Genus in future
368 //logger.warn("Genus ref type for Viral Name not implemented yet");
369 }
370
371 }
372
373
374 @SuppressWarnings("unchecked")
375 private INomenclaturalAuthor makeNameCitation(Element elNameCitation, MapWrapper<Person> authorMap, ResultWrapper<Boolean> success){
376 INomenclaturalAuthor result = null;
377 String childName;
378 boolean obligatory;
379 if (elNameCitation != null){
380 Namespace ns = elNameCitation.getNamespace();
381
382 childName = "Authors";
383 obligatory = false;
384 Element elAuthors = XmlHelp.getSingleChildElement(success, elNameCitation, childName, ns, obligatory);
385 testNoMoreElements();
386
387 if (elAuthors != null){
388 childName = "AgentName";
389 List<Element> elAgentList = elAuthors.getChildren(childName, ns);
390 Team team = Team.NewInstance();
391 result = team;
392 if (elAgentList.size() > 1){
393 for(Element elAgent : elAgentList){
394 Person teamMember = makeAgent(elAgent, ns, authorMap, success);
395 team.addTeamMember(teamMember);
396 }
397 }else if(elAgentList.size() == 1){
398 result = makeAgent(elAgentList.get(0), ns, authorMap, success);
399 }
400 }else{
401 childName = "Simple";
402 obligatory = true;
403 Element elSimple = XmlHelp.getSingleChildElement(success, elNameCitation, childName, ns, obligatory);
404 String simple = (elSimple == null)? "" : elSimple.getTextNormalize();
405 result = Team.NewInstance();
406 result.setNomenclaturalTitle(simple);
407 }
408 }
409 return result;
410 }
411
412 private Person makeAgent(Element elAgentName, Namespace ns, MapWrapper<Person> agentMap, ResultWrapper<Boolean> success){
413 Person result = null;
414 if (elAgentName != null){
415 String authorTitle = elAgentName.getTextNormalize();
416 result = Person.NewTitledInstance(authorTitle);
417 Class<? extends Person> clazz = Person.class;
418 result = makeReferenceType(elAgentName, clazz, agentMap, success);
419 return result;
420 }else{
421 return null;
422 }
423 }
424
425 private void makeCanonicalAuthorship(TaxonNameBase name, Element elCanonicalAuthorship, MapWrapper<Person> authorMap, ResultWrapper<Boolean> success){
426 if (elCanonicalAuthorship != null){
427 Namespace ns = elCanonicalAuthorship.getNamespace();
428
429 if (name instanceof NonViralName){
430 NonViralName nonViralName = (NonViralName)name;
431
432 String childName = "Simple";
433 boolean obligatory = true;
434 Element elSimple = XmlHelp.getSingleChildElement(success, elCanonicalAuthorship, childName, ns, obligatory);
435 String simple = (elSimple == null)? "" : elSimple.getTextNormalize();
436 //TODO
437 //logger.warn("authorship cache cache protected not yet implemented");
438 //nonViralName.setAuthorshipCache(simple, cacheProtected);
439
440 childName = "Authorship";
441 obligatory = false;
442 Element elAuthorship = XmlHelp.getSingleChildElement(success, elCanonicalAuthorship, childName, ns, obligatory);
443 INomenclaturalAuthor author = makeNameCitation(elAuthorship, authorMap, success);
444 nonViralName.setCombinationAuthorTeam(author);
445 testNoMoreElements();
446
447 childName = "BasionymAuthorship";
448 obligatory = false;
449 Element elBasionymAuthorship = XmlHelp.getSingleChildElement(success, elCanonicalAuthorship, childName, ns, obligatory);
450 INomenclaturalAuthor basionymAuthor = makeNameCitation(elBasionymAuthorship, authorMap, success);
451 nonViralName.setBasionymAuthorTeam(basionymAuthor);
452 testNoMoreElements();
453
454 childName = "CombinationAuthorship";
455 obligatory = false;
456 Element elCombinationAuthorship = XmlHelp.getSingleChildElement(success, elCanonicalAuthorship, childName, ns, obligatory);
457 INomenclaturalAuthor combinationAuthor = makeNameCitation(elCombinationAuthorship, authorMap ,success);
458 if (combinationAuthor != null){
459 nonViralName.setCombinationAuthorTeam(combinationAuthor);
460 }
461 testNoMoreElements();
462
463 if (elAuthorship != null && (elBasionymAuthorship != null || elCombinationAuthorship != null) ){
464 logger.warn("Authorship and (BasionymAuthorship or CombinationAuthorship) must not exist at the same time in CanonicalAuthorship");
465 success.setValue(false);
466 }
467 }
468 }
469 }
470
471
472 private void makePublishedIn(TaxonNameBase name, Element elPublishedIn, MapWrapper<Reference> referenceMap, ResultWrapper<Boolean> success){
473 if (elPublishedIn != null && name != null){
474 Class<? extends Reference> clazz = Reference.class;
475 Reference ref = makeReferenceType(elPublishedIn, clazz, referenceMap, success);
476 if (ref instanceof INomenclaturalReference){
477 name.setNomenclaturalReference(ref);
478 }else{
479 logger.warn("Reference is not of type INomenclaturalReference and could not be added to the name " + name.getTitleCache());
480 }
481 }else if (name == null){
482 logger.warn("TaxonName must not be 'null'");
483 success.setValue(false);
484 }
485 }
486
487
488 private void makeYear(TaxonNameBase name, Element elYear, ResultWrapper<Boolean> success){
489 if (elYear != null){
490 String year = elYear.getTextNormalize();
491 if (name instanceof ZoologicalName){
492 ((ZoologicalName)name).setPublicationYear(getIntegerYear(year));
493 }else{
494 logger.warn("Year can be set only for a zoological name");
495 }
496 }
497 }
498
499 private Integer getIntegerYear(String year){
500 try {
501 Integer result = Integer.valueOf(year);
502 return result;
503 } catch (NumberFormatException e) {
504 logger.warn("Year string could not be parsed. Set = 9999 instead");
505 return 9999;
506 }
507 }
508
509
510 private void makeMicroReference(TaxonNameBase name, Element elMicroReference, ResultWrapper<Boolean> success){
511 if (elMicroReference != null){
512 String microReference = elMicroReference.getTextNormalize();
513 name.setNomenclaturalMicroReference(microReference);
514 }
515 }
516
517
518 private void makeTypification(TaxonNameBase name, Element elTypifiacation, ResultWrapper<Boolean> success){
519 if (elTypifiacation != null){
520 //logger.warn("makeTypification not yet implemented");
521 //success.setValue(false);
522 }
523 }
524
525
526 private void makePublicationStatus(TaxonNameBase name, Element elPublicationStatus, ResultWrapper<Boolean> success){
527 //Status
528
529 if (elPublicationStatus != null){
530 //logger.warn("makePublicationStatus not yet implemented");
531 //success.setValue(false);
532 }
533 }
534
535 private void makeProviderLink(TaxonNameBase name, Element elProviderLink, ResultWrapper<Boolean> success){
536 if (elProviderLink != null){
537 //logger.warn("makeProviderLink not yet implemented");
538 //success.setValue(false);
539 }
540 }
541
542
543 private void makeProviderSpecificData(TaxonNameBase name, Element elProviderSpecificData, ResultWrapper<Boolean> success){
544 if (elProviderSpecificData != null){
545 //logger.warn("makeProviderSpecificData not yet implemented");
546 //success.setValue(false);
547 }
548 }
549
550
551 /* (non-Javadoc)
552 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#isIgnore(eu.etaxonomy.cdm.io.common.IImportConfigurator)
553 */
554 protected boolean isIgnore(TcsXmlImportState state){
555 return ! state.getConfig().isDoTaxonNames();
556 }
557
558 }