Project

General

Profile

Download (20.4 KB) Statistics
| Branch: | Tag: | Revision:
1
// $Id$
2
/**
3
* Copyright (C) 2007 EDIT
4
* European Distributed Institute of Taxonomy 
5
* http://www.e-taxonomy.eu
6
* 
7
* The contents of this file are subject to the Mozilla Public License Version 1.1
8
* See LICENSE.TXT at the top of this package for the full license terms.
9
*/
10

    
11
package eu.etaxonomy.cdm.api.service;
12

    
13
import java.util.ArrayList;
14
import java.util.Collection;
15
import java.util.HashSet;
16
import java.util.List;
17
import java.util.Map;
18
import java.util.Set;
19
import java.util.UUID;
20

    
21
import org.apache.log4j.Logger;
22
import org.springframework.beans.factory.annotation.Autowired;
23
import org.springframework.security.access.prepost.PreAuthorize;
24
import org.springframework.stereotype.Service;
25
import org.springframework.transaction.annotation.Propagation;
26
import org.springframework.transaction.annotation.Transactional;
27

    
28
import eu.etaxonomy.cdm.api.service.pager.Pager;
29
import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
30
import eu.etaxonomy.cdm.common.IProgressMonitor;
31
import eu.etaxonomy.cdm.model.common.Annotation;
32
import eu.etaxonomy.cdm.model.common.Language;
33
import eu.etaxonomy.cdm.model.common.MarkerType;
34
import eu.etaxonomy.cdm.model.common.TermVocabulary;
35
import eu.etaxonomy.cdm.model.description.DescriptionBase;
36
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
37
import eu.etaxonomy.cdm.model.description.Distribution;
38
import eu.etaxonomy.cdm.model.description.Feature;
39
import eu.etaxonomy.cdm.model.description.FeatureTree;
40
import eu.etaxonomy.cdm.model.description.PresenceAbsenceTermBase;
41
import eu.etaxonomy.cdm.model.description.Scope;
42
import eu.etaxonomy.cdm.model.description.TaxonDescription;
43
import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
44
import eu.etaxonomy.cdm.model.description.TextData;
45
import eu.etaxonomy.cdm.model.location.NamedArea;
46
import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
47
import eu.etaxonomy.cdm.model.media.Media;
48
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
49
import eu.etaxonomy.cdm.model.taxon.Taxon;
50
import eu.etaxonomy.cdm.persistence.dao.common.ITermVocabularyDao;
51
import eu.etaxonomy.cdm.persistence.dao.description.IDescriptionDao;
52
import eu.etaxonomy.cdm.persistence.dao.description.IDescriptionElementDao;
53
import eu.etaxonomy.cdm.persistence.dao.description.IFeatureDao;
54
import eu.etaxonomy.cdm.persistence.dao.description.IFeatureNodeDao;
55
import eu.etaxonomy.cdm.persistence.dao.description.IFeatureTreeDao;
56
import eu.etaxonomy.cdm.persistence.dao.description.IStatisticalMeasurementValueDao;
57
import eu.etaxonomy.cdm.persistence.query.OrderHint;
58
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
59

    
60
/**
61
 * @author a.mueller
62
 * @created 24.06.2008
63
 * @version 1.0
64
 */
65
@Service
66
@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
67
public class DescriptionServiceImpl extends IdentifiableServiceBase<DescriptionBase,IDescriptionDao> implements IDescriptionService {
68
 	
69
	private static final Logger logger = Logger.getLogger(DescriptionServiceImpl.class);
70

    
71
	protected IDescriptionElementDao descriptionElementDao;
72
	protected IFeatureTreeDao featureTreeDao;
73
	protected IFeatureNodeDao featureNodeDao;
74
	protected IFeatureDao featureDao;
75
	protected ITermVocabularyDao vocabularyDao;
76
	protected IStatisticalMeasurementValueDao statisticalMeasurementValueDao;
77
	//TODO change to Interface
78
	private NaturalLanguageGenerator naturalLanguageGenerator;
79
	
80
	@Autowired
81
	protected void setFeatureTreeDao(IFeatureTreeDao featureTreeDao) {
82
		this.featureTreeDao = featureTreeDao;
83
	}
84
	
85
	@Autowired
86
	protected void setFeatureNodeDao(IFeatureNodeDao featureNodeDao) {
87
		this.featureNodeDao = featureNodeDao;
88
	}
89
	
90
	@Autowired
91
	protected void setFeatureDao(IFeatureDao featureDao) {
92
		this.featureDao = featureDao;
93
	}
94
	
95
	@Autowired
96
	protected void setVocabularyDao(ITermVocabularyDao vocabularyDao) {
97
		this.vocabularyDao = vocabularyDao;
98
	}
99
	
100
	@Autowired
101
	protected void statisticalMeasurementValueDao(IStatisticalMeasurementValueDao statisticalMeasurementValueDao) {
102
		this.statisticalMeasurementValueDao = statisticalMeasurementValueDao;
103
	}
104
	
105
	@Autowired
106
	protected void setDescriptionElementDao(IDescriptionElementDao descriptionElementDao) {
107
		this.descriptionElementDao = descriptionElementDao;
108
	}
109
	
110
	@Autowired
111
	protected void setNaturalLanguageGenerator(NaturalLanguageGenerator naturalLanguageGenerator) {
112
		this.naturalLanguageGenerator = naturalLanguageGenerator;
113
	}
114
	
115
	/**
116
	 * 
117
	 */
118
	public DescriptionServiceImpl() {
119
		logger.debug("Load DescriptionService Bean");
120
	}
121
	
122
	
123

    
124
	/* (non-Javadoc)
125
	 * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)
126
	 */
127
	@Override
128
	public void updateTitleCache(Class<? extends DescriptionBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<DescriptionBase> cacheStrategy, IProgressMonitor monitor) {
129
		if (clazz == null){
130
			clazz = DescriptionBase.class;
131
		}
132
		super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
133
	}
134

    
135
	
136
	public TermVocabulary<Feature> getDefaultFeatureVocabulary(){
137
		String uuidFeature = "b187d555-f06f-4d65-9e53-da7c93f8eaa8";
138
		UUID featureUuid = UUID.fromString(uuidFeature);
139
		return (TermVocabulary)vocabularyDao.findByUuid(featureUuid);
140
	}
141

    
142
	@Autowired
143
	protected void setDao(IDescriptionDao dao) {
144
		this.dao = dao;
145
	}
146

    
147
	public int count(Class<? extends DescriptionBase> type, Boolean hasImages, Boolean hasText,Set<Feature> feature) {
148
		return dao.countDescriptions(type, hasImages, hasText, feature);
149
	}
150

    
151
	/**
152
	 * FIXME Candidate for harmonization
153
	 * rename -> getElements
154
	 */
155
	public Pager<DescriptionElementBase> getDescriptionElements(DescriptionBase description,
156
			Set<Feature> features, Class<? extends DescriptionElementBase> type, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
157

    
158
		List<DescriptionElementBase> results = listDescriptionElements(description, features, type, pageSize, pageNumber, propertyPaths);
159
		return new DefaultPagerImpl<DescriptionElementBase>(pageNumber, results.size(), pageSize, results);
160
	}
161

    
162
	public List<DescriptionElementBase> listDescriptionElements(DescriptionBase description,
163
			Set<Feature> features, Class<? extends DescriptionElementBase> type, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
164
		Integer numberOfResults = dao.countDescriptionElements(description, features, type);
165
		
166
		List<DescriptionElementBase> results = new ArrayList<DescriptionElementBase>();
167
		if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
168
			results = dao.getDescriptionElements(description, features, type, pageSize, pageNumber, propertyPaths);
169
		}
170
		return results;
171
	}
172
	
173
	public Pager<Annotation> getDescriptionElementAnnotations(DescriptionElementBase annotatedObj, MarkerType status, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){
174
		Integer numberOfResults = descriptionElementDao.countAnnotations(annotatedObj, status);
175
		
176
		List<Annotation> results = new ArrayList<Annotation>();
177
		if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
178
			results = descriptionElementDao.getAnnotations(annotatedObj, status, pageSize, pageNumber, orderHints, propertyPaths); 
179
		}
180
		
181
		return new DefaultPagerImpl<Annotation>(pageNumber, numberOfResults, pageSize, results);
182
	}
183
	
184
	
185

    
186
	public Pager<Media> getMedia(DescriptionElementBase descriptionElement,	Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
187
        Integer numberOfResults = descriptionElementDao.countMedia(descriptionElement);
188
		
189
		List<Media> results = new ArrayList<Media>();
190
		if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
191
			results = descriptionElementDao.getMedia(descriptionElement, pageSize, pageNumber, propertyPaths); 
192
		}
193
		
194
		return new DefaultPagerImpl<Media>(pageNumber, numberOfResults, pageSize, results);
195
	}
196

    
197
	public Pager<TaxonDescription> getTaxonDescriptions(Taxon taxon, Set<Scope> scopes, Set<NamedArea> geographicalScope, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
198
        Integer numberOfResults = dao.countTaxonDescriptions(taxon, scopes, geographicalScope);
199
		
200
		List<TaxonDescription> results = new ArrayList<TaxonDescription>();
201
		if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
202
			results = dao.getTaxonDescriptions(taxon, scopes, geographicalScope, pageSize, pageNumber, propertyPaths); 
203
		}
204
		
205
		return new DefaultPagerImpl<TaxonDescription>(pageNumber, numberOfResults, pageSize, results);
206
	}
207
	
208
	public List<TaxonDescription> listTaxonDescriptions(Taxon taxon, Set<Scope> scopes, Set<NamedArea> geographicalScope, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
209
		List<TaxonDescription> results = dao.getTaxonDescriptions(taxon, scopes, geographicalScope, pageSize, pageNumber, propertyPaths);
210
		return results;
211
	}
212
	
213
	public DistributionTree getOrderedDistributions(
214
			Set<TaxonDescription> taxonDescriptions,
215
			Set<NamedAreaLevel> omitLevels,
216
			List<String> propertyPaths){
217
		
218
		DistributionTree tree = new DistributionTree();
219
		List<Distribution> distList = new ArrayList<Distribution>();
220
		
221
		for (TaxonDescription taxonDescription : taxonDescriptions) {
222
			taxonDescription = (TaxonDescription) dao.load(taxonDescription.getUuid(), propertyPaths);
223
			Set<DescriptionElementBase> elements = taxonDescription.getElements();
224
			for (DescriptionElementBase element : elements) {
225
					if (element.isInstanceOf(Distribution.class)) {
226
						Distribution distribution = (Distribution) element;
227
						if(distribution.getArea() != null){
228
							distList.add(distribution);							
229
						}
230
					}
231
			}
232
		}
233
		
234
		//ordering the areas
235
		tree.merge(distList, omitLevels);
236
		tree.sortChildren();
237
		return tree;
238
	}
239

    
240
	public Pager<TaxonNameDescription> getTaxonNameDescriptions(TaxonNameBase name, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
241
        Integer numberOfResults = dao.countTaxonNameDescriptions(name);
242
		
243
		List<TaxonNameDescription> results = new ArrayList<TaxonNameDescription>();
244
		if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
245
			results = dao.getTaxonNameDescriptions(name, pageSize, pageNumber,propertyPaths); 
246
		}
247
		
248
		return new DefaultPagerImpl<TaxonNameDescription>(pageNumber, numberOfResults, pageSize, results);
249
	}
250

    
251
	
252
	public Pager<DescriptionBase> page(Class<? extends DescriptionBase> type, Boolean hasImages, Boolean hasText, Set<Feature> feature, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
253
        Integer numberOfResults = dao.countDescriptions(type, hasImages, hasText, feature);
254
		
255
		List<DescriptionBase> results = new ArrayList<DescriptionBase>();
256
		if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
257
			results = dao.listDescriptions(type, hasImages, hasText, feature, pageSize, pageNumber,orderHints,propertyPaths); 
258
		}
259
		
260
		return new DefaultPagerImpl<DescriptionBase>(pageNumber, numberOfResults, pageSize, results);
261
	}
262

    
263
	/**
264
	 * FIXME Candidate for harmonization
265
	 * Rename: searchByDistribution
266
	 */
267
	public Pager<TaxonDescription> searchDescriptionByDistribution(Set<NamedArea> namedAreas, PresenceAbsenceTermBase presence,	Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
268
        Integer numberOfResults = dao.countDescriptionByDistribution(namedAreas, presence);
269
		
270
		List<TaxonDescription> results = new ArrayList<TaxonDescription>();
271
		if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
272
			results = dao.searchDescriptionByDistribution(namedAreas, presence, pageSize, pageNumber,orderHints,propertyPaths); 
273
		}
274
		
275
		return new DefaultPagerImpl<TaxonDescription>(pageNumber, numberOfResults, pageSize, results);
276
	}
277

    
278
	/**
279
	 * FIXME Candidate for harmonization
280
	 * move: descriptionElementService.search
281
	 */
282
	public Pager<DescriptionElementBase> searchElements(Class<? extends DescriptionElementBase> clazz, String queryString, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
283
        Integer numberOfResults = descriptionElementDao.count(clazz,queryString);
284
		
285
		List<DescriptionElementBase> results = new ArrayList<DescriptionElementBase>();
286
		if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
287
			results = descriptionElementDao.search(clazz, queryString, pageSize, pageNumber, orderHints, propertyPaths); 
288
		}
289
		
290
		return new DefaultPagerImpl<DescriptionElementBase>(pageNumber, numberOfResults, pageSize, results);
291
	}
292

    
293
	/**
294
	 * FIXME Candidate for harmonization
295
	 * descriptionElementService.find
296
	 */
297
	public DescriptionElementBase getDescriptionElementByUuid(UUID uuid) {
298
		return descriptionElementDao.findByUuid(uuid);
299
	}	
300
	
301
	/**
302
	 * FIXME Candidate for harmonization
303
	 * descriptionElementService.load
304
	 */
305
	public DescriptionElementBase loadDescriptionElement(UUID uuid,	List<String> propertyPaths) {
306
		return descriptionElementDao.load(uuid, propertyPaths);
307
	}
308

    
309
    /**
310
     * FIXME Candidate for harmonization
311
     * descriptionElementService.save
312
     */
313
	@Transactional(readOnly = false)
314
	@PreAuthorize("hasRole('ALL.ADMIN') or hasPermission(#descriptionElement, 'CREATE')" )
315
	public UUID saveDescriptionElement(DescriptionElementBase descriptionElement) {
316
		return descriptionElementDao.save(descriptionElement);
317
	}
318
	
319
    /**
320
     * FIXME Candidate for harmonization
321
     * descriptionElementService.save
322
     */
323
	@Transactional(readOnly = false)
324
	@PreAuthorize("hasRole('ALL.ADMIN') or hasPermission(#descriptionElements, 'CREATE')" )
325
	public Map<UUID, DescriptionElementBase> saveDescriptionElement(Collection<DescriptionElementBase> descriptionElements) {
326
		return descriptionElementDao.saveAll(descriptionElements);
327
	}
328

    
329
    /**
330
     * FIXME Candidate for harmonization
331
     * descriptionElementService.delete
332
     */
333
	public UUID deleteDescriptionElement(DescriptionElementBase descriptionElement) {
334
		return descriptionElementDao.delete(descriptionElement);
335
	}
336

    
337
	public TermVocabulary<Feature> getFeatureVocabulary(UUID uuid) {
338
		return (TermVocabulary)vocabularyDao.findByUuid(uuid);
339
	}
340

    
341
	public <T extends DescriptionElementBase> List<T> getDescriptionElementsForTaxon(
342
			Taxon taxon, Set<Feature> features,
343
			Class<? extends T> type, Integer pageSize,
344
			Integer pageNumber, List<String> propertyPaths) {
345
		//FIXME remove cast
346
		return (List<T>) dao.getDescriptionElementForTaxon(taxon, features, type, pageSize, pageNumber, propertyPaths);
347
	}
348
	
349
	public List<DescriptionElementBase> getDescriptionElementsForTaxon(
350
			Taxon taxon, FeatureTree featureTree,
351
			Class<? extends DescriptionElementBase> type, Integer pageSize,
352
			Integer pageNumber, List<String> propertyPaths) {
353
		 //FIXME remove cast
354
		 return (List<DescriptionElementBase>) dao.getDescriptionElementForTaxon(taxon, featureTree.getDistinctFeatures(), type, pageSize, pageNumber, propertyPaths);
355
	}
356

    
357
	/* (non-Javadoc)
358
	 * @see eu.etaxonomy.cdm.api.service.IDescriptionService#generateNaturalLanguageDescription(eu.etaxonomy.cdm.model.description.FeatureTree, eu.etaxonomy.cdm.model.description.TaxonDescription, eu.etaxonomy.cdm.model.common.Language, java.util.List)
359
	 */
360
	@Override
361
	public String generateNaturalLanguageDescription(FeatureTree featureTree,
362
			TaxonDescription description, List<Language> preferredLanguages, String separator) {
363
		
364
		Language lang = preferredLanguages.size() > 0 ? preferredLanguages.get(0) : Language.DEFAULT();
365
		
366
		description = (TaxonDescription)load(description.getUuid());
367
		featureTree = featureTreeDao.load(featureTree.getUuid());
368
		
369
		StringBuilder naturalLanguageDescription = new StringBuilder();
370
		
371
		if(description.hasStructuredData()){
372
			
373
			
374
			String lastCategory = null;
375
			String categorySeparator = ". ";
376
			
377
			List<TextData> textDataList;
378
			TextData naturalLanguageDescriptionText = null;
379
			
380
			boolean useMicroFormatQuantitativeDescriptionBuilder = false;
381
			
382
			if(useMicroFormatQuantitativeDescriptionBuilder){
383
				
384
				MicroFormatQuantitativeDescriptionBuilder micro = new MicroFormatQuantitativeDescriptionBuilder();
385
				naturalLanguageGenerator.setQuantitativeDescriptionBuilder(micro);
386
				naturalLanguageDescriptionText = naturalLanguageGenerator.generateSingleTextData(featureTree, ((TaxonDescription)description), lang);
387
					
388
			} else {
389
				
390
				naturalLanguageDescriptionText = naturalLanguageGenerator.generateSingleTextData(
391
						featureTree, 
392
						((TaxonDescription)description),
393
						lang);				
394
			}
395

    
396
			return naturalLanguageDescriptionText.getText(lang);
397
			
398
//			
399
//			boolean doItBetter = false;
400
//
401
//			for (TextData textData : textDataList.toArray(new TextData[textDataList.size()])){
402
//				if(textData.getMultilanguageText().size() > 0){
403
//					
404
//					if (!textData.getFeature().equals(Feature.UNKNOWN())) {
405
//						String featureLabel = textData.getFeature().getLabel(lang);
406
//
407
//						if(doItBetter){
408
//							/*
409
//							 *  WARNING
410
//							 *  The code lines below are desinged to handle
411
//							 *  a special case where as the feature label contains 
412
//							 *  hierarchical information on the features. This code 
413
//							 *  exist only as a base for discussion, and is not 
414
//							 *  intendet to be used in production.
415
//							 */
416
//							featureLabel = StringUtils.remove(featureLabel, '>');
417
//							
418
//							String[] labelTokens = StringUtils.split(featureLabel, '<');
419
//							if(labelTokens[0].equals(lastCategory) && labelTokens.length > 1){
420
//								if(naturalLanguageDescription.length() > 0){
421
//									naturalLanguageDescription.append(separator);
422
//								}
423
//								naturalLanguageDescription.append(labelTokens[1]);
424
//							} else {
425
//								if(naturalLanguageDescription.length() > 0){
426
//									naturalLanguageDescription.append(categorySeparator);
427
//								}
428
//								naturalLanguageDescription.append(StringUtils.join(labelTokens));
429
//							}
430
//							lastCategory = labelTokens[0];
431
//							// end of demo code
432
//						} else {
433
//							if(naturalLanguageDescription.length() > 0){
434
//								naturalLanguageDescription.append(separator);
435
//							}
436
//							naturalLanguageDescription.append(textData.getFeature().getLabel(lang));							
437
//						}
438
//					} else {
439
//						if(naturalLanguageDescription.length() > 0){
440
//							naturalLanguageDescription.append(separator);
441
//						}
442
//					}
443
//					String text = textData.getMultilanguageText().values().iterator().next().getText();
444
//					naturalLanguageDescription.append(text);		
445
//					
446
//				}
447
//			}
448
		
449
		}
450
		return naturalLanguageDescription.toString();
451
	}
452

    
453
	/* (non-Javadoc)
454
	 * @see eu.etaxonomy.cdm.api.service.IDescriptionService#hasStructuredData(eu.etaxonomy.cdm.model.description.DescriptionBase)
455
	 */
456
	@Override
457
	public boolean hasStructuredData(DescriptionBase<?> description) {
458
		return load(description.getUuid()).hasStructuredData();
459
	}
460

    
461

    
462
	/* (non-Javadoc)
463
	 * @see eu.etaxonomy.cdm.api.service.IDescriptionService#moveDescriptionElementsToDescription(java.util.Collection, eu.etaxonomy.cdm.model.description.DescriptionBase, boolean)
464
	 */
465
	@Override
466
	public void moveDescriptionElementsToDescription(Collection<DescriptionElementBase> descriptionElements,
467
													DescriptionBase targetDescription, boolean isCopy) {
468
		
469
		if (descriptionElements.isEmpty() ){
470
			return ;
471
		}
472

    
473
		if (! isCopy && descriptionElements == descriptionElements.iterator().next().getInDescription().getElements()){
474
			//if the descriptionElements collection is the elements set of a description, put it in a separate set before to avoid concurrent modification exceptions
475
			descriptionElements = new HashSet<DescriptionElementBase>(descriptionElements);
476
//			descriptionElementsTmp.addAll(descriptionElements);
477
//			descriptionElements = descriptionElementsTmp;
478
		}
479
		for (DescriptionElementBase element : descriptionElements){
480
			DescriptionBase description = element.getInDescription();
481
			try {
482
				DescriptionElementBase newElement = (DescriptionElementBase)element.clone();
483
				targetDescription.addElement(newElement);	
484
			} catch (CloneNotSupportedException e) {
485
				new RuntimeException ("Clone not yet implemented for class " + element.getClass().getName(), e);
486
			}
487
			if (! isCopy){
488
				description.removeElement(element);
489
			}
490
			
491
		}
492
	}
493

    
494
}
(16-16/79)