View Javadoc

1   /**
2   * Copyright (C) 2009 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.HashSet;
13  import java.util.List;
14  import java.util.Set;
15  
16  import org.apache.log4j.Logger;
17  import org.jdom.Element;
18  import org.jdom.Namespace;
19  import org.springframework.stereotype.Component;
20  
21  import eu.etaxonomy.cdm.common.ResultWrapper;
22  import eu.etaxonomy.cdm.common.XmlHelp;
23  import eu.etaxonomy.cdm.io.common.ICdmIO;
24  import eu.etaxonomy.cdm.io.common.MapWrapper;
25  import eu.etaxonomy.cdm.io.tcsxml.TcsXmlTransformer;
26  import eu.etaxonomy.cdm.model.common.RelationshipTermBase;
27  import eu.etaxonomy.cdm.model.name.NonViralName;
28  import eu.etaxonomy.cdm.model.name.TaxonNameBase;
29  import eu.etaxonomy.cdm.model.reference.ReferenceBase;
30  import eu.etaxonomy.cdm.model.taxon.Synonym;
31  import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;
32  import eu.etaxonomy.cdm.model.taxon.Taxon;
33  import eu.etaxonomy.cdm.model.taxon.TaxonBase;
34  import eu.etaxonomy.cdm.model.taxon.TaxonNode;
35  import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
36  import eu.etaxonomy.cdm.model.taxon.TaxonomicTree;
37  import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
38  
39  
40  /**
41   * @author a.mueller
42   *
43   */
44  @Component
45  public class TcsXmlTaxonRelationsImport extends TcsXmlImportBase implements ICdmIO<TcsXmlImportState> {
46  	private static final Logger logger = Logger.getLogger(TcsXmlTaxonRelationsImport.class);
47  
48  	private static int modCount = 30000;
49  
50  	public TcsXmlTaxonRelationsImport(){
51  		super();
52  	}
53  	
54  	@Override
55  	public boolean doCheck(TcsXmlImportState state){
56  		boolean result = true;
57  		logger.warn("Checking for TaxonRelations not yet implemented");
58  		logger.warn("Creation of homotypic relations is still problematic");
59  		//result &= checkArticlesWithoutJournal(bmiConfig);
60  		//result &= checkPartOfJournal(bmiConfig);
61  		
62  		return result;
63  	}
64  	
65  	@Override
66  	public boolean doInvoke(TcsXmlImportState state){ 
67  	
68  		
69  		logger.info("start make taxon relations ...");
70  		MapWrapper<TaxonBase> taxonMap = (MapWrapper<TaxonBase>)state.getStore(ICdmIO.TAXON_STORE);
71  		MapWrapper<TaxonNameBase<?,?>> taxonNameMap = (MapWrapper<TaxonNameBase<?,?>>)state.getStore(ICdmIO.TAXONNAME_STORE);
72  		MapWrapper<ReferenceBase> referenceMap = (MapWrapper<ReferenceBase>)state.getStore(ICdmIO.REFERENCE_STORE);
73  
74  		Set<TaxonBase> taxonStore = new HashSet<TaxonBase>();
75  
76  		ResultWrapper<Boolean> success = ResultWrapper.NewInstance(true);
77  		String childName;
78  		boolean obligatory;
79  		String idNamespace = "TaxonRelation";
80  
81  		TcsXmlImportConfigurator config = state.getConfig();
82  		Element elDataSet = super.getDataSetElement(config);
83  		Namespace tcsNamespace = config.getTcsXmlNamespace();
84  		
85  		childName = "TaxonConcepts";
86  		obligatory = false;
87  		Element elTaxonConcepts = XmlHelp.getSingleChildElement(success, elDataSet, childName, tcsNamespace, obligatory);
88  		
89  		childName = "TaxonConcept";
90  		List<Element> elTaxonConceptList = elTaxonConcepts.getChildren(childName, tcsNamespace);
91  		
92  		int i = 0;
93  		int taxonRelCount = 0;
94  		
95  		//for each taxonConcept
96  		for (Element elTaxonConcept : elTaxonConceptList){
97  			if ((i++ % modCount) == 0){ logger.info("Taxa handled: " + (i-1));}
98  			taxonRelCount += makeTaxonConcept(state, taxonMap, taxonStore, elTaxonConcept, tcsNamespace, success);	
99  		}//elTaxonConcept
100 	
101 		//TaxonRelationshipAssertions
102 		taxonRelCount += makeTaxonRelationshipAssertion(state, taxonMap, referenceMap, taxonStore, elDataSet, tcsNamespace, success);	
103 		
104 		logger.info("Taxa to save: " + taxonStore.size());
105 		getTaxonService().save(taxonStore);
106 		
107 		logger.info("end make taxon relations ...");
108 		return success.getValue();
109 	}
110 	
111 	private int makeTaxonConcept(TcsXmlImportState state, MapWrapper<TaxonBase> taxonMap, Set<TaxonBase> taxonStore, Element elTaxonConcept, Namespace tcsNamespace, ResultWrapper<Boolean> success){
112 		int taxonRelCount = 0;
113 		
114 		String childName = "TaxonRelationships";
115 		boolean obligatory = false;
116 		Element elTaxonRelationships = XmlHelp.getSingleChildElement(success, elTaxonConcept, childName, tcsNamespace, obligatory);
117 		
118 		if (elTaxonRelationships != null){
119 			//Relationships
120 			String tcsElementName = "TaxonRelationship";
121 			List<Element> elTaxonRelationshipList = elTaxonRelationships.getChildren(tcsElementName, tcsNamespace);
122 
123 			for (Element elTaxonRelationship: elTaxonRelationshipList){
124 				taxonRelCount++;
125 				logger.debug("TaxonRelationship "+  taxonRelCount);
126 				
127 				String strId = elTaxonConcept.getAttributeValue("id");
128 				//TODO
129 //				String strConceptType = elTaxonConcept.getAttributeValue("type"); //original, revision, incomplete, aggregate, nominal
130 //				String strPrimary = elTaxonConcept.getAttributeValue("primary"); //If primary='true' the concept is the first level response to a query. If 'false' the concept may be a secondary concept linked directly or indirectly to the definition of a primary concept.
131 //				String strForm = elTaxonConcept.getAttributeValue("form");  //anamorph, teleomorph, hybrid
132 				
133 				TaxonBase fromTaxon = taxonMap.get(strId);
134 				makeRelationshipType(state, elTaxonRelationship, taxonMap, taxonStore, fromTaxon, success);
135 			
136 				if (fromTaxon instanceof Taxon){
137 					makeHomotypicSynonymRelations((Taxon)fromTaxon);
138 				}
139 			}// end Relationship
140 		}
141 		return taxonRelCount;
142 	}
143 
144 	private int makeTaxonRelationshipAssertion(
145 			TcsXmlImportState state, 
146 			MapWrapper<TaxonBase> taxonMap,
147 			MapWrapper<ReferenceBase> referenceMap,
148 			Set<TaxonBase> taxonStore, 
149 			Element elDataSet, 
150 			Namespace tcsNamespace, 
151 			ResultWrapper<Boolean> success){
152 		
153 		int i = 0;
154 		String childName = "TaxonRelationshipAssertions";
155 		boolean obligatory = false;
156 		Element elTaxonRelationshipAssertions = XmlHelp.getSingleChildElement(success, elDataSet, childName, tcsNamespace, obligatory);
157 		if(elTaxonRelationshipAssertions == null){
158 			return 0;
159 		}
160 		
161 		childName = "TaxonRelationshipAssertion";
162 		List<Element> elTaxonRelationshipAssertionList = elTaxonRelationshipAssertions.getChildren(childName, tcsNamespace);
163 		//for each taxon relationship assertion
164 		for (Element elTaxonRelationshipAssertion : elTaxonRelationshipAssertionList){
165 			if ((i++ % modCount) == 0){ logger.info("TaxonRelationshipAssertions handled: " + (i-1));}
166 			String strId = elTaxonRelationshipAssertion.getAttributeValue("id");
167 			//TODO id
168 			
169 			childName = "AccordingTo";
170 			obligatory = true;
171 			Element elAccordingTo = XmlHelp.getSingleChildElement(success, elTaxonRelationshipAssertion, childName, tcsNamespace, obligatory);
172 			ReferenceBase ref = makeAccordingTo(elAccordingTo, referenceMap, success);
173 			
174 			childName = "FromTaxonConcept";
175 			obligatory = true;
176 			Element elFromTaxonConcept = XmlHelp.getSingleChildElement(success, elTaxonRelationshipAssertion, childName, tcsNamespace, obligatory);
177 
178 			Class<? extends TaxonBase> clazz = Taxon.class;
179 			//TODO if synonym
180 			TaxonBase fromTaxon = makeReferenceType(elFromTaxonConcept, clazz, taxonMap, success);
181 			
182 			makeRelationshipType(state, elTaxonRelationshipAssertion, taxonMap, taxonStore, fromTaxon, success);
183 		}//elTaxonRelationshipAssertion
184 		
185 		return i;
186 	}
187 	
188 	
189 	/**
190 	 * Handles the TCS RelationshipType element.
191 	 * @param tcsConfig
192 	 * @param elRelationship
193 	 * @param taxonMap
194 	 * @param taxonStore
195 	 * @param fromTaxon
196 	 * @param success
197 	 */
198 	private void makeRelationshipType(
199 			TcsXmlImportState state
200 			, Element elRelationship 
201 			, MapWrapper<TaxonBase> taxonMap
202 			, Set<TaxonBase> taxonStore
203 			, TaxonBase fromTaxon
204 			, ResultWrapper<Boolean> success
205 			){
206 	
207 		if (elRelationship == null){
208 			success.setValue(false);
209 		}
210 		String strRelType = elRelationship.getAttributeValue("type");
211 		
212 		
213 		try {
214 			ResultWrapper<Boolean> isInverse = new ResultWrapper<Boolean>();
215 			isInverse.setValue(false);
216 			RelationshipTermBase<?> relType = TcsXmlTransformer.tcsRelationshipType2Relationship(strRelType, isInverse);
217 			
218 			//toTaxon (should be part of relationshipType)
219 			boolean isSynonym = (relType instanceof SynonymRelationshipType);
220 			TaxonBase toTaxon = getToTaxon(elRelationship, taxonMap, isSynonym, success);
221 			
222 			if (toTaxon != null && fromTaxon != null){
223 				//exchange taxa if relationship is inverse
224 				if (isInverse.getValue() == true ){
225 					TaxonBase tmp = toTaxon;
226 					toTaxon = fromTaxon;
227 					fromTaxon = tmp;
228 				}
229 				
230 				//Create relationship
231 				if (! (toTaxon instanceof Taxon)){
232 					logger.warn("TaxonBase toTaxon is not of Type 'Taxon'. Relationship is not added.");
233 					success.setValue(false);
234 				}else{
235 					Taxon taxonTo = (Taxon)toTaxon;
236 					ReferenceBase citation = null;
237 					String microReference = null;
238 					if (relType instanceof SynonymRelationshipType){
239 						SynonymRelationshipType synRelType = (SynonymRelationshipType)relType;
240 						if (! (fromTaxon instanceof Synonym )){
241 							logger.warn("TaxonBase fromTaxon is not of Type 'Synonym'. Relationship is not added.");
242 							success.setValue(false);
243 						}else{
244 							Synonym synonym = (Synonym)fromTaxon;
245 							TaxonNameBase<?,?> synName = synonym.getName();
246 							TaxonNameBase<?,?> accName = taxonTo.getName();
247 							if (synName != null && accName != null && synName.isHomotypic(accName)
248 										&& ( synRelType.equals(SynonymRelationshipType.SYNONYM_OF()))){
249 								synRelType = SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF(); 
250 							}
251 							if (! relationExists(taxonTo, synonym, synRelType)){
252 								taxonTo.addSynonym(synonym, synRelType,  citation, microReference);	
253 							}else{
254 								//TODO citation, microReference
255 								//TODO different synRelTypes -> warning
256 								success.setValue(false);
257 							}
258 						}
259 					}else if (relType instanceof TaxonRelationshipType){
260 						makeTaxonRelationship(state, (TaxonRelationshipType)relType, fromTaxon, taxonTo, citation, microReference, success);
261 					}else{
262 						logger.warn("Unknown Relationshiptype");
263 						success.setValue(false);
264 					}
265 					taxonStore.add(toTaxon);
266 				}
267 			}else{
268 				if (toTaxon == null){
269 					logger.warn("toTaxon (" + /*strToTaxon + */ ") could  not be found in taxonMap. Relationship of type " + strRelType + " was not added to CDM");
270 				}
271 				if (fromTaxon == null){
272 					logger.warn("fromTaxon (" + /*strTaxonAbout + */") could not be found in taxonMap. Relationship was not added to CDM");
273 				}
274 				success.setValue(false);
275 			}
276 			
277 		} catch (UnknownCdmTypeException e) {
278 			//TODO
279 			logger.warn("relationshipType " + strRelType + " not yet implemented");
280 			success.setValue(false);
281 		}
282 		return;
283 	}
284 	
285 	private void makeTaxonRelationship(TcsXmlImportState state, TaxonRelationshipType relType, TaxonBase fromTaxon, Taxon taxonTo, ReferenceBase citation, String microReference, ResultWrapper<Boolean> success){
286 		TaxonRelationshipType taxRelType = (TaxonRelationshipType)relType;
287 		if (! (fromTaxon instanceof Taxon )){
288 			logger.warn("TaxonBase fromTaxon " + /*strTaxonAbout +*/ "is not of Type 'Taxon'. Relationship is not added.");
289 			success.setValue(false);
290 		}else{
291 			Taxon taxonFrom = (Taxon)fromTaxon;
292 			if (relType.equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN())){
293 				makeTaxonomicallyIncluded(state, taxonTo, taxonFrom, citation, microReference);
294 			}
295 			taxonFrom.addTaxonRelation(taxonTo, taxRelType, citation, microReference);
296 		}
297 	}
298 	
299 	private boolean makeTaxonomicallyIncluded(TcsXmlImportState state, Taxon toTaxon, Taxon fromTaxon, ReferenceBase citation, String microCitation){
300 		ReferenceBase sec = toTaxon.getSec();
301 		TaxonomicTree tree = state.getTree(sec);
302 		if (tree == null){
303 			tree = makeTree(state, sec);
304 		}
305 		TaxonNode childNode = tree.addParentChild(toTaxon, fromTaxon, citation, microCitation);
306 		return (childNode != null);
307 	}
308 	
309 	
310 	private TaxonBase getToTaxon(Element elTaxonRelationship, MapWrapper<TaxonBase> map, boolean isSynonym, ResultWrapper<Boolean> success){
311 		TaxonBase result = null;
312 		if (elTaxonRelationship == null || map == null){
313 			success.setValue(false);
314 		}else{
315 			String childName = "ToTaxonConcept";
316 			boolean obligatory = true;
317 			Element elToTaxonConcept = XmlHelp.getSingleChildElement(success, elTaxonRelationship, childName, elTaxonRelationship.getNamespace(), obligatory);
318 			
319 			String linkType = elToTaxonConcept.getAttributeValue("linkType");
320 			if (linkType == null || linkType.equals("local")){
321 				String ref = elToTaxonConcept.getAttributeValue("ref");
322 				if (ref != null){
323 					result = map.get(ref);
324 				}else{
325 					String title = elToTaxonConcept.getTextNormalize();
326 					//TODO synonym?
327 					TaxonNameBase<?,?> taxonName = NonViralName.NewInstance(null);
328 					taxonName.setTitleCache(title, true);
329 					logger.warn("Free text related taxon seems to be bug in TCS");
330 					if (isSynonym){
331 						result = Synonym.NewInstance(taxonName, TcsXmlTaxonImport.unknownSec());
332 					}else{
333 						result = Taxon.NewInstance(taxonName, TcsXmlTaxonImport.unknownSec());	
334 					}
335 					result.setTitleCache(title, true);
336 				}
337 			}else{
338 				logger.warn("External link types for synonym not yet implemented");
339 			}
340 		}
341 		return result;
342 	}
343 	
344 
345 	
346 	
347 	
348 	private boolean relationExists(Taxon taxonTo, Synonym synonym, SynonymRelationshipType synRelType){
349 		if (synonym == null){
350 			return false;
351 		}
352 		if (synonym.getRelationType(taxonTo).size() > 0){
353 			Set<SynonymRelationshipType> relTypeList = synonym.getRelationType(taxonTo);
354 			if (relTypeList.contains(synRelType)){
355 				return true;
356 			}else{
357 				logger.warn("Taxon-Synonym pair has 2 different SynonymRelationships. This is against the rules");
358 				return false;
359 			}
360 		}else{
361 			return false;
362 		}
363 	}
364 
365 	private boolean makeHomotypicSynonymRelations(Taxon aboutTaxon){
366 		TaxonNameBase<?,?> aboutName = aboutTaxon.getName();
367 		if (aboutName != null){
368 			Set<TaxonNameBase> typifiedNames = aboutName.getHomotypicalGroup().getTypifiedNames();
369 			for (TaxonNameBase<?,?> typifiedName : typifiedNames){
370 				//TODO check if name is part of this tcs file
371 				if (typifiedName.equals(aboutName)){
372 					continue;
373 				}
374 				Set<Synonym> syns = typifiedName.getSynonyms();
375 				for(Synonym syn:syns){
376 					aboutTaxon.addSynonym(syn, SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF());
377 				}
378 			}
379 		}
380 		return true;
381 	}
382 	
383 	/* (non-Javadoc)
384 	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#isIgnore(eu.etaxonomy.cdm.io.common.IImportConfigurator)
385 	 */
386 	protected boolean isIgnore(TcsXmlImportState state){
387 		return ! state.getConfig().isDoRelTaxa();
388 	}
389 	
390 }