Project

General

Profile

Download (20.6 KB) Statistics
| Branch: | Revision:
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
package eu.etaxonomy.cdm.io.pesi.out.old;
10

    
11
import java.sql.Connection;
12
import java.sql.PreparedStatement;
13
import java.sql.SQLException;
14
import java.util.Arrays;
15
import java.util.List;
16
import java.util.Map;
17
import java.util.Set;
18

    
19
import org.apache.log4j.Logger;
20
import org.joda.time.DateTime;
21
import org.springframework.stereotype.Component;
22
import org.springframework.transaction.TransactionStatus;
23

    
24
import eu.etaxonomy.cdm.io.common.DbExportStateBase;
25
import eu.etaxonomy.cdm.io.common.Source;
26
import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
27
import eu.etaxonomy.cdm.io.common.mapping.out.IdMapper;
28
import eu.etaxonomy.cdm.io.common.mapping.out.MethodMapper;
29
import eu.etaxonomy.cdm.io.pesi.out.PesiExportBase;
30
import eu.etaxonomy.cdm.io.pesi.out.PesiExportConfigurator;
31
import eu.etaxonomy.cdm.io.pesi.out.PesiExportMapping;
32
import eu.etaxonomy.cdm.io.pesi.out.PesiExportState;
33
import eu.etaxonomy.cdm.io.pesi.out.PesiTransformer;
34
import eu.etaxonomy.cdm.model.common.CdmBase;
35
import eu.etaxonomy.cdm.model.common.Extension;
36
import eu.etaxonomy.cdm.model.common.ExtensionType;
37
import eu.etaxonomy.cdm.model.common.Language;
38
import eu.etaxonomy.cdm.model.common.LanguageString;
39
import eu.etaxonomy.cdm.model.description.CommonTaxonName;
40
import eu.etaxonomy.cdm.model.description.DescriptionBase;
41
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
42
import eu.etaxonomy.cdm.model.description.Distribution;
43
import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
44
import eu.etaxonomy.cdm.model.description.TaxonDescription;
45
import eu.etaxonomy.cdm.model.description.TaxonInteraction;
46
import eu.etaxonomy.cdm.model.description.TextData;
47
import eu.etaxonomy.cdm.model.location.NamedArea;
48
import eu.etaxonomy.cdm.model.name.TaxonName;
49
import eu.etaxonomy.cdm.model.taxon.Taxon;
50
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
51

    
52
/**
53
 * The export class for {@link eu.etaxonomy.cdm.model.description.DescriptionElementBase DescriptionElements}.<p>
54
 * Inserts into DataWarehouse database table <code>Note</code>.<p>
55
 * It is divided into two phases:<ul>
56
 * <li>Phase 1:	Export of DescriptionElements as Notes.
57
 * <li>Phase 2:	Export of TaxonName extensions <code>taxComment</code>, <code>fauComment</code> and <code>fauExtraCodes</code> as Notes.</ul>
58
 * @author e.-m.lee
59
 * @date 23.02.2010
60
 *
61
 */
62
@Component
63
public class PesiNoteExport extends PesiExportBase {
64
	private static final Logger logger = Logger.getLogger(PesiNoteExport.class);
65
	private static final Class<? extends CdmBase> standardMethodParameter = DescriptionElementBase.class;
66

    
67
	private static int modCount = 1000;
68
	private static final String dbTableName = "Note";
69
	private static final String pluralString = "Notes";
70
	private static final String parentPluralString = "Taxa";
71

    
72
	public PesiNoteExport() {
73
		super();
74
	}
75

    
76
	/* (non-Javadoc)
77
	 * @see eu.etaxonomy.cdm.io.common.DbExportBase#getStandardMethodParameter()
78
	 */
79
	@Override
80
	public Class<? extends CdmBase> getStandardMethodParameter() {
81
		return standardMethodParameter;
82
	}
83

    
84
	/* (non-Javadoc)
85
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
86
	 */
87
	@Override
88
	protected boolean doCheck(PesiExportState state) {
89
		boolean result = true;
90
		return result;
91
	}
92

    
93
	/* (non-Javadoc)
94
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doInvoke(eu.etaxonomy.cdm.io.common.IoStateBase)
95
	 */
96
	@Override
97
	protected void doInvoke(PesiExportState state) {
98
		try {
99
			logger.info("*** Started Making " + pluralString + " ...");
100

    
101
			// Get the limit for objects to save within a single transaction.
102
			int limit = state.getConfig().getLimitSave();
103

    
104
			// Stores whether this invoke was successful or not.
105
			boolean success = true;
106

    
107
			// PESI: Clear the database table Note.
108
			doDelete(state);
109

    
110
			// Start transaction
111
			success &= doPhase01(state);
112

    
113

    
114
			logger.info("PHASE 2...");
115
			doPhase02(state, limit);
116

    
117

    
118
			logger.info("*** Finished Making " + pluralString + " ..." + getSuccessString(success));
119

    
120
			if (!success){
121
			    state.getResult().addError("An error occurred in PesiNoteExport");
122
			}
123
			return;
124
		} catch (SQLException e) {
125
			e.printStackTrace();
126
			logger.error(e.getMessage());
127
			state.getResult().addException(e);
128
		}
129
	}
130

    
131
	//PHASE 01: Description Elements
132
	private boolean doPhase01(PesiExportState state) throws SQLException {
133
		logger.info("PHASE 1...");
134
		int count = 0;
135
		int pastCount = 0;
136
		 boolean success = true;
137

    
138
		 // Calculate the pageNumber
139
		int pageNumber = 1;
140
		int pageSize = 1000;
141

    
142

    
143
		// Get specific mappings: (CDM) DescriptionElement -> (PESI) Note
144
		PesiExportMapping mapping = getMapping();
145

    
146
		// Initialize the db mapper
147
		mapping.initialize(state);
148

    
149

    
150
		List<DescriptionElementBase> list = null;
151

    
152
		TransactionStatus txStatus = startTransaction(true);
153
		logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + pageSize + ") ...");
154
		List<String> propPath = Arrays.asList(new String[]{"inDescription.taxon"});
155
		while ((list = getDescriptionService().listDescriptionElements(null, null, null, pageSize, pageNumber, propPath)).size() > 0) {
156

    
157
			logger.info("Fetched " + list.size() + " " + pluralString + ". Exporting...");
158
			for (DescriptionElementBase descriptionElement : list) {
159
				if (getNoteCategoryFk(descriptionElement) != null) {
160
					doCount(count++, modCount, pluralString);
161
					success &= mapping.invoke(descriptionElement);
162
				}
163
			}
164

    
165
			// Commit transaction
166
			commitTransaction(txStatus);
167
			logger.debug("Committed transaction.");
168
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
169
			pastCount = count;
170

    
171
			// Start transaction
172
			txStatus = startTransaction(true);
173
			logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + pageSize + ") ...");
174

    
175
			// Increment pageNumber
176
			pageNumber++;
177
		}
178
		if (list.size() == 0) {
179
			logger.info("No " + pluralString + " left to fetch.");
180
		}
181
		// Commit transaction
182
		commitTransaction(txStatus);
183
		logger.info("Committed transaction.");
184
		return success;
185
	}
186

    
187
	//PHASE 02: Taxa extensions
188
	private void doPhase02(PesiExportState state, int limit) {
189
		TransactionStatus txStatus;
190
		txStatus = startTransaction(true);
191
		ExtensionType taxCommentExtensionType = (ExtensionType)getTermService().find(PesiTransformer.taxCommentUuid);
192
		ExtensionType fauCommentExtensionType = (ExtensionType)getTermService().find(PesiTransformer.fauCommentUuid);
193
		ExtensionType fauExtraCodesExtensionType = (ExtensionType)getTermService().find(PesiTransformer.fauExtraCodesUuid);
194
		List<TaxonBase> taxonBaseList = null;
195

    
196
		int count = 0;
197
		int pastCount = 0;
198
		Connection connection = state.getConfig().getDestination().getConnection();
199
		logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
200
		//logger.warn("TODO handle extensions on taxon level, not name level (");
201
		while ((taxonBaseList = getTaxonService().list(null, limit, count, null, null)).size() > 0) {
202

    
203
			logger.info("Fetched " + taxonBaseList.size() + " names. Exporting...");
204
			for (TaxonBase<?> taxon : taxonBaseList) {
205
				Set<Extension> extensions = taxon.getExtensions();
206
				for (Extension extension : extensions) {
207
					if (extension.getType().equals(taxCommentExtensionType)) {
208
						String taxComment = extension.getValue();
209
						invokeNotes(taxComment,
210
								PesiTransformer.getNoteCategoryFk(PesiTransformer.taxCommentUuid),
211
								PesiTransformer.getNoteCategoryCache(PesiTransformer.taxCommentUuid),
212
								null, null, getTaxonFk(taxon.getName(), state),connection);
213
					} else if (extension.getType().equals(fauCommentExtensionType)) {
214
						String fauComment = extension.getValue();
215
						invokeNotes(fauComment,
216
								PesiTransformer.getNoteCategoryFk(PesiTransformer.fauCommentUuid),
217
								PesiTransformer.getNoteCategoryCache(PesiTransformer.fauCommentUuid),
218
								null, null, getTaxonFk(taxon.getName(), state),connection);
219
					} else if (extension.getType().equals(fauExtraCodesExtensionType)) {
220
						String fauExtraCodes = extension.getValue();
221
						invokeNotes(fauExtraCodes,
222
								PesiTransformer.getNoteCategoryFk(PesiTransformer.fauExtraCodesUuid),
223
								PesiTransformer.getNoteCategoryCache(PesiTransformer.fauExtraCodesUuid),
224
								null, null, getTaxonFk(taxon.getName(), state),connection);
225
					}
226
				}
227

    
228
				doCount(count++, modCount, pluralString);
229
			}
230

    
231
			// Commit transaction
232
			commitTransaction(txStatus);
233
			logger.debug("Committed transaction.");
234
			logger.info("Exported " + (count - pastCount) + " names. Total: " + count);
235
			pastCount = count;
236

    
237
			// Start transaction
238
			txStatus = startTransaction(true);
239
			logger.info("Started new transaction. Fetching some taxa first (max: " + limit + ") ...");
240
		}
241
		if (taxonBaseList.size() == 0) {
242
			logger.info("No taxa left to fetch.");
243
		}
244
		// Commit transaction
245
		commitTransaction(txStatus);
246
		logger.debug("Committed transaction.");
247
	}
248

    
249
	/**
250
	 * @param taxComment
251
	 * @param noteCategoryFk
252
	 * @param noteCategoryCache
253
	 * @param object
254
	 * @param object2
255
	 */
256
	private void invokeNotes(String note, Integer noteCategoryFk,
257
			String noteCategoryCache, Integer languageFk, String languageCache,
258
			Integer taxonFk, Connection connection) {
259
		String notesSql = "UPDATE Note SET Note_1 = ?, NoteCategoryFk = ?, NoteCategoryCache = ?, LanguageFk = ?, LanguageCache = ? WHERE TaxonFk = ?";
260
		try {
261
			PreparedStatement notesStmt = connection.prepareStatement(notesSql);
262

    
263
			if (note != null) {
264
				notesStmt.setString(1, note);
265
			} else {
266
				notesStmt.setObject(1, null);
267
			}
268

    
269
			if (noteCategoryFk != null) {
270
				notesStmt.setInt(2, noteCategoryFk);
271
			} else {
272
				notesStmt.setObject(2, null);
273
			}
274

    
275
			if (noteCategoryCache != null) {
276
				notesStmt.setString(3, noteCategoryCache);
277
			} else {
278
				notesStmt.setObject(3, null);
279
			}
280

    
281
			if (languageFk != null) {
282
				notesStmt.setInt(4, languageFk);
283
			} else {
284
				notesStmt.setObject(4, null);
285
			}
286

    
287
			if (languageCache != null) {
288
				notesStmt.setString(5, languageCache);
289
			} else {
290
				notesStmt.setObject(5, null);
291
			}
292

    
293
			if (taxonFk != null) {
294
				notesStmt.setInt(6, taxonFk);
295
			} else {
296
				notesStmt.setObject(6, null);
297
			}
298

    
299
			notesStmt.executeUpdate();
300
		} catch (SQLException e) {
301
			logger.error("Note could not be created: " + note);
302
			e.printStackTrace();
303
		}
304

    
305

    
306
	}
307

    
308
	/**
309
	 * Deletes all entries of database tables related to <code>Note</code>.
310
	 * @param state The PesiExportState
311
	 * @return Whether the delete operation was successful or not.
312
	 */
313
	protected boolean doDelete(PesiExportState state) {
314
		PesiExportConfigurator pesiConfig = state.getConfig();
315

    
316
		String sql;
317
		Source destination =  pesiConfig.getDestination();
318

    
319
		// Clear NoteSource
320
		sql = "DELETE FROM NoteSource";
321
		destination.setQuery(sql);
322
		destination.update(sql);
323

    
324
		// Clear Note
325
		sql = "DELETE FROM " + dbTableName;
326
		destination.setQuery(sql);
327
		destination.update(sql);
328
		return true;
329
	}
330

    
331
	/* (non-Javadoc)
332
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#isIgnore(eu.etaxonomy.cdm.io.common.IoStateBase)
333
	 */
334
	@Override
335
	protected boolean isIgnore(PesiExportState state) {
336
		return ! state.getConfig().isDoNotes();
337
	}
338

    
339
	/**
340
	 * Returns the <code>Note_1</code> attribute.
341
	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
342
	 * @return The <code>Note_1</code> attribute.
343
	 * @see MethodMapper
344
	 */
345
	@SuppressWarnings("unused")
346
	private static String getNote_1(DescriptionElementBase descriptionElement) {
347
		String result = null;
348

    
349
		if (descriptionElement.isInstanceOf(TextData.class)) {
350
			TextData textData = CdmBase.deproxy(descriptionElement, TextData.class);
351
			result = textData.getText(Language.DEFAULT());
352
		}
353

    
354
		return result;
355
	}
356

    
357
	/**
358
	 * Returns the <code>Note_2</code> attribute.
359
	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
360
	 * @return The <code>Note_2</code> attribute.
361
	 * @see MethodMapper
362
	 */
363
	@SuppressWarnings("unused")
364
	private static String getNote_2(DescriptionElementBase descriptionElement) {
365
		logger.warn("Not yet implemented");
366
		return null;
367
	}
368

    
369
	/**
370
	 * Returns the <code>NoteCategoryFk</code> attribute.
371
	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
372
	 * @return The <code>NoteCategoryFk</code> attribute.
373
	 * @see MethodMapper
374
	 */
375
	private static Integer getNoteCategoryFk(DescriptionElementBase descriptionElement) {
376
		Integer result = null;
377
		result = PesiTransformer.feature2NoteCategoryFk(descriptionElement.getFeature());
378
		return result;
379
	}
380

    
381
	/**
382
	 * Returns the <code>NoteCategoryCache</code> attribute.
383
	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
384
	 * @return The <code>NoteCategoryCache</code> attribute.
385
	 * @see MethodMapper
386
	 */
387
	@SuppressWarnings("unused")
388
	private static String getNoteCategoryCache(DescriptionElementBase descriptionElement, PesiExportState state) {
389
		return state.getTransformer().getCacheByFeature(descriptionElement.getFeature());
390
	}
391

    
392
	/**
393
	 * Returns the <code>LanguageFk</code> attribute.
394
	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
395
	 * @return The <code>LanguageFk</code> attribute.
396
	 * @see MethodMapper
397
	 */
398
	@SuppressWarnings("unused")
399
	private static Integer getLanguageFk(DescriptionElementBase descriptionElement) {
400
		Language language = getLanguage(descriptionElement);
401

    
402
		return PesiTransformer.language2LanguageId(language);
403
	}
404

    
405
	/**
406
	 * Returns the <code>LanguageCache</code> attribute.
407
	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
408
	 * @return The <code>LanguageCache</code> attribute.
409
	 * @throws UndefinedTransformerMethodException
410
	 * @see MethodMapper
411
	 */
412
	@SuppressWarnings("unused")
413
	private static String getLanguageCache(DescriptionElementBase descriptionElement, PesiExportState state) throws UndefinedTransformerMethodException {
414
		Language language = getLanguage(descriptionElement);
415
		return state.getTransformer().getCacheByLanguage(language);
416
	}
417

    
418
	private static Language getLanguage(DescriptionElementBase descriptionElement) {
419
		Language language = null;
420

    
421
		Map<Language, LanguageString> multilanguageText = null;
422
		if (descriptionElement.isInstanceOf(CommonTaxonName.class)) {
423
			CommonTaxonName commonTaxonName = CdmBase.deproxy(descriptionElement, CommonTaxonName.class);
424
			language = commonTaxonName.getLanguage();
425
		} else if (descriptionElement.isInstanceOf(TextData.class)) {
426
			TextData textData = CdmBase.deproxy(descriptionElement, TextData.class);
427
			multilanguageText = textData.getMultilanguageText();
428
		} else if (descriptionElement.isInstanceOf(IndividualsAssociation.class)) {
429
			IndividualsAssociation individualsAssociation = CdmBase.deproxy(descriptionElement, IndividualsAssociation.class);
430
			multilanguageText = individualsAssociation.getDescription();
431
		} else if (descriptionElement.isInstanceOf(TaxonInteraction.class)) {
432
			TaxonInteraction taxonInteraction = CdmBase.deproxy(descriptionElement, TaxonInteraction.class);
433
			multilanguageText = taxonInteraction.getDescription();
434
		} else {
435
			logger.debug("Given descriptionElement does not support languages. Hence LanguageCache could not be determined: " + descriptionElement.getUuid());
436
		}
437

    
438
		if (multilanguageText != null) {
439
			Set<Language> languages = multilanguageText.keySet();
440

    
441
			// TODO: Think of something more sophisticated than this
442
			if (languages.size() > 0) {
443
				language = languages.iterator().next();
444
			}
445
			if (languages.size() > 1){
446
				logger.warn("There is more than 1 language for a given description (" + descriptionElement.getClass().getSimpleName() + "):" + descriptionElement.getUuid());
447
			}
448
		}
449
		return language;
450
	}
451

    
452
	/**
453
	 * Returns the <code>Region</code> attribute.
454
	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
455
	 * @return The <code>Region</code> attribute.
456
	 * @see MethodMapper
457
	 */
458
	@SuppressWarnings("unused")
459
	private static String getRegion(DescriptionElementBase descriptionElement) {
460
		String result = null;
461
		DescriptionBase<?> inDescription = descriptionElement.getInDescription();
462

    
463
		try {
464
			// Area information are associated to TaxonDescriptions and Distributions.
465
			if (descriptionElement.isInstanceOf(Distribution.class)) {
466
				Distribution distribution = CdmBase.deproxy(descriptionElement, Distribution.class);
467
				//TODO not working any more after transformer refactoring
468
				result = new PesiTransformer(null).getCacheByNamedArea(distribution.getArea());
469
			} else if (inDescription != null && inDescription.isInstanceOf(TaxonDescription.class)) {
470
				TaxonDescription taxonDescription = CdmBase.deproxy(inDescription, TaxonDescription.class);
471
				Set<NamedArea> namedAreas = taxonDescription.getGeoScopes();
472
				if (namedAreas.size() == 1) {
473
					result = new PesiTransformer(null).getCacheByNamedArea(namedAreas.iterator().next());
474
				} else if (namedAreas.size() > 1) {
475
					logger.warn("This TaxonDescription contains more than one NamedArea: " + taxonDescription.getTitleCache());
476
				}
477
			}
478
		} catch (ClassCastException e) {
479
			// TODO Auto-generated catch block
480
			e.printStackTrace();
481
		} catch (UndefinedTransformerMethodException e) {
482
			// TODO Auto-generated catch block
483
			e.printStackTrace();
484
		}
485
		return result;
486
	}
487

    
488
	/**
489
	 * Returns the <code>TaxonFk</code> attribute.
490
	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
491
	 * @param state The {@link PesiExportState PesiExportState}.
492
	 * @return The <code>TaxonFk</code> attribute.
493
	 * @see MethodMapper
494
	 */
495
	@SuppressWarnings("unused")
496
	private static Integer getTaxonFk(DescriptionElementBase descriptionElement, DbExportStateBase<?, PesiTransformer> state) {
497
		Integer result = null;
498
		DescriptionBase<?> inDescription = descriptionElement.getInDescription();
499
		if (inDescription != null && inDescription.isInstanceOf(TaxonDescription.class)) {
500
			TaxonDescription taxonDescription = CdmBase.deproxy(inDescription, TaxonDescription.class);
501
			Taxon taxon = taxonDescription.getTaxon();
502
			result = state.getDbId(taxon.getName());
503
		}
504
		return result;
505
	}
506

    
507
	/**
508
	 * Returns the TaxonFk for a given TaxonName.
509
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
510
	 * @param state The {@link DbExportStateBase DbExportState}.
511
	 * @return
512
	 */
513
	private static Integer getTaxonFk(TaxonName taxonName, DbExportStateBase<?, PesiTransformer> state) {
514
		return state.getDbId(taxonName);
515
	}
516

    
517
	/**
518
	 * Returns the <code>LastAction</code> attribute.
519
	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
520
	 * @return The <code>LastAction</code> attribute.
521
	 * @see MethodMapper
522
	 */
523
	@SuppressWarnings("unused")
524
	private static String getLastAction(DescriptionElementBase descriptionElement) {
525
		// TODO
526
		return null;
527
	}
528

    
529
	/**
530
	 * Returns the <code>LastActionDate</code> attribute.
531
	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
532
	 * @return The <code>LastActionDate</code> attribute.
533
	 * @see MethodMapper
534
	 */
535
	@SuppressWarnings("unused")
536
	private static DateTime getLastActionDate(DescriptionElementBase descriptionElement) {
537
		DateTime result = null;
538
		return result;
539
	}
540

    
541
	/**
542
	 * Returns the CDM to PESI specific export mappings.
543
	 * @return The {@link PesiExportMapping PesiExportMapping}.
544
	 */
545
	private PesiExportMapping getMapping() {
546
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
547

    
548
		mapping.addMapper(IdMapper.NewInstance("NoteId"));
549
		mapping.addMapper(MethodMapper.NewInstance("Note_1", this));
550
		mapping.addMapper(MethodMapper.NewInstance("Note_2", this));
551
		mapping.addMapper(MethodMapper.NewInstance("NoteCategoryFk", this));
552
		mapping.addMapper(MethodMapper.NewInstance("NoteCategoryCache", this));
553
		mapping.addMapper(MethodMapper.NewInstance("LanguageFk", this));
554
		mapping.addMapper(MethodMapper.NewInstance("LanguageCache", this));
555
		mapping.addMapper(MethodMapper.NewInstance("Region", this));
556
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk", this.getClass(), "getTaxonFk", standardMethodParameter, DbExportStateBase.class));
557
		mapping.addMapper(MethodMapper.NewInstance("LastAction", this));
558
		mapping.addMapper(MethodMapper.NewInstance("LastActionDate", this));
559

    
560
		return mapping;
561
	}
562

    
563
}
(3-3/6)