Révision 3690

TXM/trunk/bundles/org.txm.core/src/java/org/txm/core/results/TXMResult.java (revision 3690)
5 5
import java.io.IOException;
6 6
import java.lang.reflect.Constructor;
7 7
import java.lang.reflect.Field;
8
import java.lang.reflect.InvocationTargetException;
8 9
import java.nio.file.Path;
9 10
import java.text.DateFormat;
10 11
import java.text.SimpleDateFormat;
......
73 74
 * 
74 75
 */
75 76
public abstract class TXMResult implements Cloneable, Comparable<TXMResult> {
76
	
77

  
77 78
	public static final DateFormat ID_TIME_FORMAT = new SimpleDateFormat("yyyyMMdd_HHmmssSSS"); //$NON-NLS-1$
78
	
79

  
79 80
	public static final DateFormat PRETTY_TIME_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); //$NON-NLS-1$
80
	
81

  
81 82
	public static final DateFormat PRETTY_LOCALIZED_TIME_FORMAT = new SimpleDateFormat("dd MMMM yyyy, HH'h'mm", Locale.getDefault()); //$NON-NLS-1$
82
	
83

  
83 84
	public static final String UUID_PREFIX = "txm_res_"; //$NON-NLS-1$
84
	
85

  
85 86
	public static final Pattern FILE_NAME_PATTERN = Pattern.compile("[^a-zA-Z0-9\\.-_]+"); //$NON-NLS-1$
86
	
87

  
87 88
	public static final String UNDERSCORE = "_"; //$NON-NLS-1$
88
	
89

  
89 90
	/**
90 91
	 * Results counter to create unique ids.
91 92
	 */
92 93
	private static int next = 0;
93
	
94

  
94 95
	/**
95 96
	 * The weight, essentially used for sorting purpose.
96 97
	 */
97 98
	protected int weight;
98
	
99

  
99 100
	/**
100 101
	 * the command this command to true when the result needs to be recomputed.
101 102
	 * like after updating parameters
102 103
	 */
103 104
	private boolean dirty = true;
104
	
105

  
105 106
	/**
106 107
	 * If a method changed the internal data without **recomputing** the result, the result must be marked "altered"
107 108
	 */
108 109
	private boolean altered = false;
109
	
110

  
110 111
	/**
111 112
	 * The result parent. Should not be null except for roots TXMResult.
112 113
	 */
113 114
	protected TXMResult parent;
114
	
115

  
115 116
	/**
116 117
	 * Children results.
117 118
	 */
118 119
	protected List<TXMResult> children;
119
	
120

  
120 121
	/**
121 122
	 * Command preferences node path.
122 123
	 */
123 124
	protected String commandPreferencesNodePath;
124
	
125

  
125 126
	/**
126 127
	 * Parameters node path. Concatenation of the Project scope path + the result uuid
127 128
	 */
128 129
	private String parametersNodePath;
129
	
130

  
130 131
	/**
131 132
	 * Thread used when calling compute().
132 133
	 */
133 134
	protected Thread computingThread;
134
	
135

  
135 136
	/**
136 137
	 * To manage the computing cancel with TreadDeath.
137 138
	 */
138 139
	// FIXME: SJ: became useless? 
139 140
	private Semaphore progressSemaphore = new Semaphore(1);
140
	
141

  
141 142
	/**
142 143
	 * To keep track of the parameters used for the last computing and to determine if the result is dirty.
143 144
	 * Also permits to optimize computing by testing if a specific parameter value has changed since last computing and skip or not some computing steps.
144 145
	 */
145 146
	protected ArrayList<HashMap<String, Object>> parametersHistory = null;
146
	
147

  
147 148
	/**
148 149
	 * User persistable state.
149 150
	 */
150 151
	@Parameter(key = TXMPreferences.PERSITABLE, type = Parameter.INTERNAL)
151 152
	private boolean userPersistable;
152
	
153

  
153 154
	/**
154 155
	 * The visibility state.
155 156
	 */
156 157
	@Parameter(key = TXMPreferences.VISIBLE, type = Parameter.INTERNAL)
157 158
	private Boolean visible;
158
	
159

  
159 160
	/**
160 161
	 * The user name (to rename a result).
161 162
	 */
162 163
	@Parameter(key = TXMPreferences.USER_NAME, type = Parameter.INTERNAL)
163 164
	private String userName;
164
	
165

  
165 166
	/**
166 167
	 * To store the simple name for unserialization and lazy loading.
167 168
	 * This string can be used, for example, to display some information in UI even if the result has not yet been recomputed.
......
169 170
	// TODO: SJ: we should do the same with getDetails() method
170 171
	@Parameter(key = TXMPreferences.LAZY_NAME, type = Parameter.INTERNAL)
171 172
	private String lazyName;
172
	
173

  
173 174
	/**
174 175
	 * Last computing date.
175 176
	 */
176 177
	@Parameter(key = TXMPreferences.LAST_COMPUTING_DATE, type = Parameter.INTERNAL)
177 178
	private Date lastComputingDate;
178
	
179

  
179 180
	/**
180 181
	 * Creation date.
181 182
	 */
182 183
	@Parameter(key = TXMPreferences.CREATION_DATE, type = Parameter.INTERNAL)
183 184
	private Date creationDate;
184
	
185

  
185 186
	/**
186 187
	 * If locked, the result is not updated when computed.
187 188
	 */
188 189
	@Parameter(key = TXMPreferences.LOCK, type = Parameter.INTERNAL)
189 190
	private Boolean locked = false;
190
	
191

  
191 192
	/**
192 193
	 * Current computing state.
193 194
	 * True if the result is in the process of computing (is in compute(,) method)
194 195
	 */
195 196
	private boolean computing = false;
196
	
197

  
197 198
	/**
198 199
	 * Synchronization state between this result and its parent.
199 200
	 * If true, computing the parent will always also computes this child.
200 201
	 */
201 202
	protected boolean synchronizedWithParent = false;
202
	
203
	
203

  
204

  
204 205
	/**
205 206
	 * Creates a new TXMResult, child of the specified parent.
206 207
	 * 
......
209 210
	public TXMResult(TXMResult parent) {
210 211
		this(null, parent);
211 212
	}
212
	
213

  
213 214
	/**
214 215
	 * Creates a new TXMResult with no parent.
215 216
	 * If a local preference node exists with parent_parameters_node_path value, the parent will be retrieved and this result will be added to it.
......
219 220
	public TXMResult(String parametersNodePath) {
220 221
		this(parametersNodePath, null);
221 222
	}
222
	
223

  
223 224
	/**
224 225
	 * Creates a new TXMResult, child of the specified parent.
225 226
	 * If the parameters node path is null, a new path is generated from the project root path ending by a new generated UUID.
......
234 235
		if (parent != null) {
235 236
			parent.addChild(this);
236 237
		}
237
		
238

  
238 239
		// new result
239 240
		if (parametersNodePath == null) {
240 241
			if (this.getProject() != null) {
......
243 244
			else {
244 245
				parametersNodePath = ""; //$NON-NLS-1$
245 246
			}
246
			
247

  
247 248
			this.parametersNodePath = parametersNodePath + createUUID() + "_" + this.getClass().getSimpleName(); //$NON-NLS-1$ ;
248 249
		}
249 250
		// lazy result (persistence / import) or linked result ("send to" commands)
250 251
		else {
251 252
			this.parametersNodePath = parametersNodePath;
252 253
		}
253
		
254

  
254 255
		Log.finest("TXMResult.TXMResult(): parameters node path: " + this.parametersNodePath + "."); //$NON-NLS-1$ //$NON-NLS-2$
255
		
256

  
256 257
		this.weight = 0;
257
		
258

  
258 259
		this.children = new ArrayList<>(1);
259 260
		// this.children = Collections.synchronizedList(new ArrayList<TXMResult>()); // FIXME: SJ: can fix the concurrent exception if needed when accessing children from multiple threads
260
		
261

  
261 262
		this.commandPreferencesNodePath = FrameworkUtil.getBundle(getClass()).getSymbolicName();
262 263
		Log.finest("TXMResult.TXMResult(): command preferences node path: " + this.commandPreferencesNodePath + "."); //$NON-NLS-1$ //$NON-NLS-2$
263
		
264

  
264 265
		this.visible = true;
265 266
		this.dirty = true;
266
		
267

  
267 268
		// set result as persistent if AUTO_PERSISTENCE_ENABLED preference is activated
268 269
		if (TBXPreferences.getInstance().getBoolean(TBXPreferences.AUTO_PERSISTENCE_ENABLED)) {
269 270
			this.saveParameter(TXMPreferences.PERSITABLE, true);
270 271
		}
271
		
272

  
272 273
		// retrieving parent from UUID/node path
273 274
		String parentNodePath = this.getStringParameterValue(TXMPreferences.PARENT_PARAMETERS_NODE_PATH);
274
		
275

  
275 276
		// System.out.println("TXMResult.TXMResult(): parent UUID = " + parentUUID);
276
		
277
		 // search for parent only if UUID != "ROOT"
277

  
278
		// search for parent only if UUID != "ROOT"
278 279
		if (!("ROOT".equals(this.parametersNodePath)) && //$NON-NLS-1$
279 280
				parent == null &&
280 281
				this.parametersNodePath != null &&
281 282
				!parentNodePath.isEmpty()) {
282
			
283

  
283 284
			Log.finest("Searching parent with UUID/node path " + parentNodePath + "..."); //$NON-NLS-1$ //$NON-NLS-2$
284
			
285

  
285 286
			TXMResult retrievedParent = null;
286 287
			if (this.getProject() != null) {
287 288
				retrievedParent = TXMResult.getResult(this.getProject(), parentNodePath); // works only if parent and child are in the same project (which should be).
......
301 302
					TBXPreferences.delete(this);
302 303
				}
303 304
				catch (Exception e) {
304
					
305

  
305 306
				}
306 307
				return;
307 308
			}
308 309
		}
309
		
310

  
310 311
		// loads parameters from local result node, current command preferences or default command preferences
311 312
		try {
312 313
			this.autoLoadParametersFromAnnotations(); // auto fill from Parameter annotations
313 314
			this.loadParameters(); // subclasses manual settings
314
			
315

  
315 316
			try {
316 317
				if (this.creationDate == null) {
317 318
					this.creationDate = Calendar.getInstance().getTime();
......
327 328
			Log.severe("Fail to load the " + parametersNodePath + " result: " + e); //$NON-NLS-1$ //$NON-NLS-2$
328 329
			Log.printStackTrace(e);
329 330
		}
330
		
331

  
331 332
		// Log
332 333
		if (this.parent == null) {
333 334
			Log.finest("Warning: the TXMResult of " + this.getClass() + " is attached to no parent. (uuid = " + this.parametersNodePath + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-1$
334 335
		}
335 336
	}
336
	
337

  
337 338
	/**
338 339
	 * Sets the user name.
339 340
	 * 
......
342 343
	public void setUserName(String name) {
343 344
		this.userName = name;
344 345
	}
345
	
346

  
346 347
	/**
347 348
	 * @return true if the internal data has been modified
348 349
	 */
349 350
	public boolean isAltered() {
350 351
		return altered;
351 352
	}
352
	
353

  
353 354
	/**
354 355
	 * Mark the result as altered -> modifications are lost after a re-compute
355 356
	 */
356 357
	public void setAltered() {
357 358
		this.altered = true;
358 359
	}
359
	
360

  
360 361
	/**
361 362
	 * Do something when project is closed
362 363
	 */
363 364
	public void onProjectClose() {
364
		
365

  
365 366
	}
366
	
367

  
367 368
	/**
368 369
	 * Do something when project is opened
369 370
	 */
370 371
	public void onProjectOpen() {
371
		
372

  
372 373
	}
373
	
374

  
374 375
	/**
375 376
	 * Locks/unlocks the result.
376 377
	 */
377 378
	public void setLocked(boolean state) {
378 379
		this.locked = state;
379
		
380

  
380 381
		if (this.locked && this.parent != null) {
381 382
			this.parent.setLocked(true);
382 383
		}
......
386 387
			}
387 388
		}
388 389
	}
389
	
390

  
390 391
	/**
391 392
	 * @return true if the result is locked
392 393
	 */
393 394
	public boolean isLocked() {
394 395
		return this.locked;
395 396
	}
396
	
397

  
397 398
	/**
398 399
	 * Creates an UUID dedicated to preferences/parameters storing and to persistence of a result.
399 400
	 * 
......
402 403
	public static String createUUID() {
403 404
		return UUID_PREFIX + ID_TIME_FORMAT.format(new Date(System.currentTimeMillis())) + "_" + nextInt();
404 405
	}
405
	
406

  
406 407
	/**
407 408
	 * 
408 409
	 * @return the next integer in the current session of TXM
......
410 411
	private static synchronized String nextInt() {
411 412
		return String.format("%04d", next++); //$NON-NLS-1$
412 413
	}
413
	
414

  
414 415
	/**
415 416
	 * Checks if the result has at least one child.
416 417
	 * 
......
419 420
	public boolean hasChildren() {
420 421
		return this.children.size() > 0;
421 422
	}
422
	
423
	
423

  
424

  
424 425
	/**
425 426
	 * Gets the preferences node path of the result linked command.
426 427
	 * 
......
429 430
	public String getCommandPreferencesNodePath() {
430 431
		return this.commandPreferencesNodePath;
431 432
	}
432
	
433
	
433

  
434

  
434 435
	public String[] getCommandPreferencesKeys() throws BackingStoreException {
435 436
		return TXMPreferences.getCommandScopeKeys(this.commandPreferencesNodePath);
436 437
	}
437
	
438

  
438 439
	public String[] getDefaultPreferencesKeys() throws BackingStoreException {
439 440
		return TXMPreferences.getDefaultScopeKeys(this.commandPreferencesNodePath);
440 441
	}
441
	
442

  
442 443
	/**
443 444
	 * Gets a current parameter specified by its annotation "key".
444 445
	 * 
......
449 450
	public Object getParameter(String key) {
450 451
		return getParameter(key, false);
451 452
	}
452
	
453

  
453 454
	/**
454 455
	 * Gets a current parameter specified by its annotation "key".
455 456
	 * 
......
474 475
		}
475 476
		return null;
476 477
	}
477
	
478

  
478 479
	/**
479 480
	 * Stores the parameters of the specified types used during last computing in the specified HashMap.
480 481
	 * 
......
483 484
	 * @throws Exception
484 485
	 */
485 486
	protected void updateLastParameters(ArrayList<Integer> parametersTypes, HashMap<String, Object> lastParameters) throws Exception {
486
		
487

  
487 488
		List<Field> fields = BeanParameters.getAllFields(this);
488
		
489

  
489 490
		for (Field f : fields) {
490 491
			Parameter parameter = f.getAnnotation(Parameter.class);
491 492
			if (parameter == null || !parametersTypes.contains(parameter.type())) {
492 493
				continue;
493 494
			}
494
			
495

  
495 496
			String name;
496 497
			if (!parameter.key().isEmpty()) {
497 498
				name = parameter.key();
......
499 500
			else {
500 501
				name = f.getName();
501 502
			}
502
			
503

  
503 504
			f.setAccessible(true);
504 505
			lastParameters.put(name, f.get(this));
505 506
		}
506
		
507

  
507 508
		// if (lastParameters.isEmpty()) {
508 509
		// System.out.println("TXMResult.updateLastParameters() empty");
509 510
		// }
510
		
511

  
511 512
		if (this.parametersHistory == null) { // 
512 513
			this.parametersHistory = new ArrayList<HashMap<String,Object>>();
513 514
		}
514 515
		this.parametersHistory.add(lastParameters);
515
		
516

  
516 517
		// truncate the stack to the max count
517 518
		// FIXME: SJ: later, store this in a TBX preference
518 519
		if (this.parametersHistory.size() > 5) {
......
521 522
			this.parametersHistory.remove(0);
522 523
		}
523 524
	}
524
	
525

  
525 526
	/**
526 527
	 * Stores the last parameters of the specified types used during last computing.
527 528
	 * 
......
531 532
	protected void updateLastParameters(ArrayList<Integer> parametersTypes) throws Exception {
532 533
		this.updateLastParameters(parametersTypes, new HashMap<>());
533 534
	}
534
	
535

  
535 536
	/**
536 537
	 * Stores the last parameters used during last computing.
537 538
	 * 
......
542 543
		parametersTypes.add(Parameter.COMPUTING);
543 544
		this.updateLastParameters(parametersTypes);
544 545
	}
545
	
546
	
546

  
547

  
547 548
	/**
548 549
	 * Removes all the parameters of the specified type from the last parameters history.
549 550
	 * 
......
551 552
	 * @throws Exception
552 553
	 */
553 554
	protected void clearLastParameters(int parameterType) {
554
		
555

  
555 556
		if (parametersHistory == null) return; // nothing to do
556
		
557

  
557 558
		List<Field> fields = BeanParameters.getAllFields(this);
558
		
559

  
559 560
		for (Field f : fields) {
560 561
			Parameter parameter = f.getAnnotation(Parameter.class);
561 562
			if (parameter == null || parameter.type() != parameterType) {
562 563
				continue;
563 564
			}
564
			
565

  
565 566
			String name;
566 567
			if (!parameter.key().isEmpty()) {
567 568
				name = parameter.key();
......
569 570
			else {
570 571
				name = f.getName();
571 572
			}
572
			
573

  
573 574
			f.setAccessible(true);
574 575
			try {
575 576
				this.getLastParametersFromHistory().remove(name);
......
578 579
				// nothing to do if the stack is empty
579 580
			}
580 581
		}
581
		
582
		
582

  
583

  
583 584
		try {
584 585
			if (this.parametersHistory != null && !this.getLastParametersFromHistory().isEmpty()) {
585 586
				this.parametersHistory.remove(this.parametersHistory.size() - 1);
......
589 590
			// nothing to do if the stack is empty
590 591
		}
591 592
	}
592
	
593
	
593

  
594

  
594 595
	/**
595 596
	 * Checks if a parameter value has changed since last computing.
596 597
	 * 
......
599 600
	 * @return
600 601
	 */
601 602
	public boolean hasParameterChanged(String key) {
602
		
603

  
603 604
		if (parametersHistory == null) return true;
604
		
605

  
605 606
		return this.hasParameterChanged(key, this.getLastParametersFromHistory());
606 607
	}
607
	
608

  
608 609
	/**
609 610
	 * Checks if a parameter value has changed since last computing according to the specified external map.
610 611
	 * 
......
613 614
	 * @return
614 615
	 */
615 616
	public boolean hasParameterChanged(String key, HashMap<String, Object> lastParameters) {
616
		
617

  
617 618
		if (lastParameters == null) {
618 619
			return true;
619 620
		}
620
		
621

  
621 622
		if (key.isEmpty()) {
622 623
			return false;
623 624
		}
624
		
625

  
625 626
		Object lastValue = lastParameters.get(key);
626 627
		Object newValue = this.getParameter(key, true);
627 628
		if (lastValue == null) {
......
636 637
			return !lastValue.equals(newValue);
637 638
		}
638 639
	}
639
	
640

  
640 641
	/**
641 642
	 * Checks if at least one parameter value of the specified parameter type has changed since last computing according to the specified external map.
642 643
	 * 
......
645 646
	 * @return
646 647
	 */
647 648
	public boolean hasParametersChanged(HashMap<String, Object> lastParameters, int parameterType) throws Exception {
648
		
649

  
649 650
		// result has never been computed
650 651
		if (lastParameters == null) {
651 652
			return true;
652 653
		}
653
		
654

  
654 655
		boolean hasParameterChanged = false;
655
		
656

  
656 657
		List<Field> fields = BeanParameters.getAllFields(this);
657
		
658

  
658 659
		for (Field f : fields) {
659 660
			Parameter parameter = f.getAnnotation(Parameter.class);
660 661
			if (parameter == null || parameter.type() != parameterType) {
......
667 668
			else {
668 669
				name = f.getName();
669 670
			}
670
			
671

  
671 672
			f.setAccessible(true); // to be able to test the field values
672
			
673

  
673 674
			hasParameterChanged = this.hasParameterChanged(name, lastParameters);
674
			
675

  
675 676
			if (hasParameterChanged) {
676 677
				Log.finest("TXMResult.hasParameterChanged(): " + this.getClass().getSimpleName() + ": parameter " + name + " has changed (type = " + parameterType + ").");
677 678
				break;
678 679
			}
679 680
		}
680
		
681

  
681 682
		return hasParameterChanged;
682 683
	}
683
	
684
	
684

  
685

  
685 686
	/**
686 687
	 * Checks if at least one computing parameter value has changed since last computing.
687 688
	 * 
......
690 691
	 */
691 692
	public boolean hasParametersChanged() throws Exception {
692 693
		if (parametersHistory == null) return true;
693
		
694

  
694 695
		return this.hasParametersChanged(this.getLastParametersFromHistory(), Parameter.COMPUTING);
695 696
	}
696
	
697
	
697

  
698

  
698 699
	/**
699 700
	 * Dumps the result current parameters, the command user preferences and the command default preferences linked to the result.
700 701
	 * 
701 702
	 * @return
702 703
	 */
703 704
	public String dumpPreferences() {
704
		
705

  
705 706
		StringBuilder str = new StringBuilder();
706
		
707

  
707 708
		if (TXMPreferences.resultScopeNodeExists(this)) {
708 709
			str.append("\nResult local node preferences\n"); //$NON-NLS-1$
709 710
			str.append(TXMPreferences.getKeysAndValues(this.getParametersNodePath()));
710 711
		}
711
		
712

  
712 713
		str.append("\nCommand preferences\n"); //$NON-NLS-1$
713 714
		str.append(TXMPreferences.getKeysAndValues(this.commandPreferencesNodePath));
714
		
715

  
715 716
		str.append("\nDefault command preferences\n"); //$NON-NLS-1$
716 717
		str.append(TXMPreferences.getKeysAndValues(DefaultScope.INSTANCE + "/" + this.commandPreferencesNodePath));
717
		
718

  
718 719
		return str.toString();
719 720
	}
720
	
721
	
721

  
722

  
722 723
	/**
723 724
	 * Dumps the result computing parameters (Parameter annotation).
724 725
	 * 
......
727 728
	public String dumpParameters() {
728 729
		return this.dumpParameters(Parameter.COMPUTING);
729 730
	}
730
	
731
	
731

  
732

  
732 733
	/**
733 734
	 * Dumps the result computing parameters (Parameter annotation).
734 735
	 * 
......
736 737
	 * @return
737 738
	 */
738 739
	public String dumpParameters(int parametersType) {
739
		
740

  
740 741
		StringBuilder str = new StringBuilder();
741
		
742

  
742 743
		str.append("Parameters (type = " + parametersType + " / " + Parameter.types[parametersType] + ")\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
743
		
744

  
744 745
		List<Field> fields = BeanParameters.getAllFields(this);
745
		
746

  
746 747
		for (Field f : fields) {
747 748
			Parameter parameter = f.getAnnotation(Parameter.class);
748 749
			if (parameter == null || parameter.type() != parametersType) {
749 750
				continue;
750 751
			}
751 752
			f.setAccessible(true);
752
			
753

  
753 754
			String name;
754 755
			if (!parameter.key().isEmpty()) {
755 756
				name = parameter.key();
......
757 758
			else {
758 759
				name = f.getName();
759 760
			}
760
			
761

  
761 762
			try {
762 763
				Object v = f.get(this);
763 764
				if (v != null) {
......
778 779
		}
779 780
		return str.toString();
780 781
	}
781
	
782
	
783
	
782

  
783

  
784

  
784 785
	// /**
785 786
	// * Updates the dirty state by comparing an old parameter with a new one.
786 787
	// *
......
796 797
	// this.setDirty();
797 798
	// }
798 799
	// }
799
	
800

  
800 801
	/**
801 802
	 * Sets the dirty state to true if a parameter has changed since last computing.
802 803
	 * 
......
857 858
		// else {
858 859
		// return this.hasParameterChanged();
859 860
		// }
860
		
861

  
861 862
		if (this.hasParametersChanged()) {
862 863
			this.dirty = true;
863 864
		}
864 865
	}
865
	
866

  
866 867
	/**
867 868
	 * Gets the list of parameters used during last computing or null if the result has not been computed yet.
868 869
	 * 
......
877 878
			return null;
878 879
		}
879 880
	}
880
	
881

  
881 882
	/**
882 883
	 * TODO seems to work but the TXMEditor.compute() method re-use the widgets values and the parametersHistory is appended with the 'new' parameters
883 884
	 * 
......
886 887
	public void revertParametersFromHistory() throws Exception {
887 888
		ArrayList<HashMap<String, Object>> history = getParametersHistory();
888 889
		if (history == null) return; // nothing to do
889
		
890

  
890 891
		if (history.size() < 2) return; // nothing to do
891
		
892

  
892 893
		history.remove(history.size() - 1);
893
		
894

  
894 895
		HashMap<String, Object> last = history.get(history.size() - 1);
895
		
896

  
896 897
		this.setParameters(last); // auto
897 898
	}
898
	
899
	
899

  
900

  
900 901
	/**
901 902
	 * Dumps the parameters history stack to the specified StringBuffer.
902 903
	 * 
......
914 915
			}
915 916
		}
916 917
	}
917
	
918
	
918

  
919

  
919 920
	/**
920 921
	 * Gets the dirty state.
921 922
	 * 
......
924 925
	public boolean isDirty() {
925 926
		return this.dirty;
926 927
	}
927
	
928

  
928 929
	/**
929 930
	 * Marks the result as dirty so editors or others will know the TXMResult needs to be recomputed.
930 931
	 * This method is also recursively called on all the children branch to mark them as dirty.
......
932 933
	public void setDirty() {
933 934
		this.setDirty(true);
934 935
	}
935
	
936

  
936 937
	/**
937 938
	 * Sets the result dirty state so editors or others will know the TXMResult needs to be recomputed or not.
938 939
	 * This method is also recursively called on all the children branch sets the new dirty state.
......
947 948
			}
948 949
		}
949 950
	}
950
	
951

  
951 952
	/**
952 953
	 * Gets the value of the specified key in parameters, local result node or
953 954
	 * default preferences nodes.
......
958 959
	public int getIntParameterValue(String key) {
959 960
		return TXMPreferences.getInt(key, this, this.commandPreferencesNodePath);
960 961
	}
961
	
962

  
962 963
	/**
963 964
	 * Gets the value of the specified key in parameters, local result node or
964 965
	 * default preferences nodes.
......
969 970
	public float getFloatParameterValue(String key) {
970 971
		return TXMPreferences.getFloat(key, this, this.commandPreferencesNodePath);
971 972
	}
972
	
973

  
973 974
	/**
974 975
	 * Gets the value of the specified key in parameters, local result node or
975 976
	 * default preferences nodes.
......
980 981
	public double getDoubleParameterValue(String key) {
981 982
		return TXMPreferences.getDouble(key, this, this.commandPreferencesNodePath);
982 983
	}
983
	
984

  
984 985
	/**
985 986
	 * Gets the value of the specified key in parameters, local result node or
986 987
	 * default preferences nodes.
......
991 992
	public boolean getBooleanParameterValue(String key) {
992 993
		return TXMPreferences.getBoolean(key, this, this.commandPreferencesNodePath);
993 994
	}
994
	
995

  
995 996
	/**
996 997
	 * Gets the value of the specified key in parameters, local result node or
997 998
	 * default preferences nodes.
......
1002 1003
	public long getLongParameterValue(String key) {
1003 1004
		return TXMPreferences.getLong(key, this, this.commandPreferencesNodePath);
1004 1005
	}
1005
	
1006

  
1006 1007
	/**
1007 1008
	 * Gets the value of the specified key in parameters, local result node or
1008 1009
	 * default preferences nodes.
......
1013 1014
	public String getStringParameterValue(String key) {
1014 1015
		return TXMPreferences.getString(key, this, this.commandPreferencesNodePath);
1015 1016
	}
1016
	
1017

  
1017 1018
	/**
1018 1019
	 * Gets the value of the specified key in parameters, local result node or
1019 1020
	 * default preferences nodes.
......
1024 1025
	public Date getDateParameterValue(String key) {
1025 1026
		return TXMPreferences.getDate(key, this, this.commandPreferencesNodePath);
1026 1027
	}
1027
	
1028

  
1028 1029
	/**
1029 1030
	 * Gets the value of the specified key in parameters, local result node or
1030 1031
	 * default preferences nodes.
......
1041 1042
			return null;
1042 1043
		}
1043 1044
	}
1044
	
1045
	
1045

  
1046

  
1046 1047
	/**
1047 1048
	 * Checks if the specified parameter exists.
1048 1049
	 * 
......
1052 1053
	public boolean parameterExists(String key) {
1053 1054
		return TXMPreferences.keyExists(this.parametersNodePath, key);
1054 1055
	}
1055
	
1056

  
1056 1057
	/**
1057 1058
	 * Checks if a preference is empty (equals to "") in the command preference node.
1058 1059
	 * An empty preference can be used to disable a functionality for a kind of result
......
1064 1065
	public boolean isEmptyPreference(String key) {
1065 1066
		return TXMPreferences.isEmpty(this.commandPreferencesNodePath, key);
1066 1067
	}
1067
	
1068

  
1068 1069
	/**
1069 1070
	 * Stores the specified parameters pairs of key/value in a local node dedicated to the specified result. The node qualifier is generated by the <code>TXMResult.getUUID</code> method.
1070 1071
	 * 
......
1074 1075
	public void saveParameter(String key, List<?> values) {
1075 1076
		this.saveParameter(key, StringUtils.join(values, UNDERSCORE));
1076 1077
	}
1077
	
1078
	
1078

  
1079

  
1079 1080
	/**
1080 1081
	 * Stores the specified parameters pairs of key/value in a local node dedicated to the specified result. The node qualifier is generated by the <code>TXMResult.getUUID</code> method.
1081 1082
	 * 
......
1086 1087
	public void saveParameter(String key, Object value) {
1087 1088
		// FIXME: debug
1088 1089
		// Log.info("TXMResult.saveParameter(): saving parameter " + key + " = " + value + " for " + this.getClass() + " (" + value.getClass() + ") to node.");
1089
		
1090

  
1090 1091
		TXMPreferences.putLocal(this, key, value);
1091 1092
	}
1092
	
1093
	
1093

  
1094

  
1094 1095
	/**
1095 1096
	 * Stores the result parameter fields declared by their @Parameter annotation in the persisted result node preferences.
1096 1097
	 * One have to implement saveParameters() to save parameters that are not of primitive types (e.g. Property, List, HashMap...)
......
1099 1100
	 * @throws Exception
1100 1101
	 */
1101 1102
	public boolean autoSaveParametersFromAnnotations() throws Exception {
1102
		
1103

  
1103 1104
		Log.finest("TXMResult.autoSaveParametersFromAnnotations(): " + this.getClass().getSimpleName() + ": saving parameters to local node...");
1104
		
1105

  
1105 1106
		// internal data to save for unserialization
1106 1107
		if (this.getClass().isAnonymousClass()) {
1107 1108
			this.saveParameter(TXMPreferences.CLASS, this.getClass().getSuperclass().getName()); // avoid bug later if an anonymous class has been used.
......
1110 1111
		}
1111 1112
		this.saveParameter(TXMPreferences.RESULT_PARAMETERS_NODE_PATH, this.parametersNodePath);
1112 1113
		this.saveParameter(TXMPreferences.BUNDLE_ID, FrameworkUtil.getBundle(getClass()).getSymbolicName());
1113
		
1114
		
1114

  
1115

  
1115 1116
		if (this.parent != null) {
1116 1117
			this.saveParameter(TXMPreferences.PARENT_PARAMETERS_NODE_PATH, this.parent.getParametersNodePath());
1117 1118
		}
1118
		
1119

  
1119 1120
		if (hasBeenComputedOnce()) {
1120 1121
			this.lazyName = this.getSimpleName();
1121 1122
		}
1122
		
1123

  
1123 1124
		List<Field> fields = BeanParameters.getAllFields(this);
1124
		
1125

  
1125 1126
		for (Field f : fields) {
1126 1127
			Parameter parameter = f.getAnnotation(Parameter.class);
1127 1128
			if (parameter == null) {
......
1131 1132
			if (key.isEmpty()) {
1132 1133
				continue; // no preference key defined
1133 1134
			}
1134
			
1135

  
1135 1136
			if (f.getType().isAssignableFrom(int.class) || f.getType().isAssignableFrom(Integer.class) ||
1136 1137
					f.getType().isAssignableFrom(double.class) || f.getType().isAssignableFrom(Double.class) ||
1137 1138
					f.getType().isAssignableFrom(float.class) || f.getType().isAssignableFrom(Float.class) ||
1138 1139
					f.getType().isAssignableFrom(boolean.class) || f.getType().isAssignableFrom(Boolean.class) ||
1139 1140
					f.getType().isAssignableFrom(String.class) || f.getType().isAssignableFrom(File.class)
1140
			
1141
			// FIXME: do not pass a Serializable for now
1142
			// || !f.getType().isAssignableFrom(Serializable.class)
1143
			// || !Serializable.class.isInstance(f)
1144
			
1145
			) {
1146
				
1147
				
1141

  
1142
					// FIXME: do not pass a Serializable for now
1143
					// || !f.getType().isAssignableFrom(Serializable.class)
1144
					// || !Serializable.class.isInstance(f)
1145

  
1146
					) {
1147

  
1148

  
1148 1149
				f.setAccessible(true); // set accessible to test the field values
1149 1150
				try {
1150 1151
					Object value = f.get(this);
......
1171 1172
		}
1172 1173
		return true;
1173 1174
	}
1174
	
1175

  
1175 1176
	/**
1176 1177
	 * Dedicated to stores the result parameters in the persisted result node preferences.
1177 1178
	 * One have to implement this method to save parameters that are not of primitive types (e.g. Property, List, HashMap...)
......
1181 1182
	 * @throws Exception
1182 1183
	 */
1183 1184
	public abstract boolean saveParameters() throws Exception;
1184
	
1185

  
1185 1186
	/**
1186 1187
	 * calls saveParameters() on this and children if childrenAsWell is set to true
1187 1188
	 * 
......
1192 1193
	 * @throws Exception
1193 1194
	 */
1194 1195
	public boolean saveParameters(boolean childrenAsWell) throws Exception {
1195
		
1196

  
1196 1197
		boolean ret = true;
1197
		
1198

  
1198 1199
		if (this.mustBePersisted()) {
1199 1200
			ret = this.saveParameters();
1200 1201
			if (childrenAsWell) {
......
1205 1206
		}
1206 1207
		return ret;
1207 1208
	}
1208
	
1209

  
1209 1210
	// FIXME: SJ: this method should not call persist on children if childrenAsWell == false?
1210 1211
	public void persist(boolean childrenAsWell) {
1211 1212
		if (this.mustBePersisted()) {
......
1215 1216
			}
1216 1217
		}
1217 1218
	}
1218
	
1219

  
1219 1220
	/**
1220 1221
	 *  load or reload parameters from the preference store node
1221 1222
	 * @return
......
1224 1225
	public boolean autoLoadParametersFromAnnotations() throws Exception {
1225 1226
		return this.autoLoadParametersFromAnnotations(Parameter.COMPUTING) && this.autoLoadParametersFromAnnotations(Parameter.INTERNAL);
1226 1227
	}
1227
	
1228

  
1228 1229
	/**
1229 1230
	 * Fills the result parameter fields declared by their @Parameter annotation from the persisted result node or default node of the preferences.
1230 1231
	 * One have to implement loadParameters() to load parameters that are not of primitive types (e.g. Property, List, HashMap...)
......
1233 1234
	 * @throws Exception
1234 1235
	 */
1235 1236
	protected boolean autoLoadParametersFromAnnotations(int parameterType) throws Exception {
1236
		
1237

  
1237 1238
		List<Field> fields = BeanParameters.getAllFields(this);
1238
		
1239

  
1239 1240
		for (Field f : fields) {
1240
			
1241

  
1241 1242
			Parameter parameter = f.getAnnotation(Parameter.class);
1242 1243
			if (parameter == null || parameter.type() != parameterType) {
1243 1244
				continue;
1244 1245
			}
1245
			
1246

  
1246 1247
			String key = parameter.key();
1247
			
1248

  
1248 1249
			if (key.isEmpty()) {
1249 1250
				continue; // no preference key defined for the parameter so it can't be filled with default value or result persistence value from preferences nodes
1250 1251
			}
1251 1252
			try {
1252 1253
				f.setAccessible(true); // set accessible to test the field values
1253
				
1254

  
1254 1255
				Object value = null;
1255
				
1256

  
1256 1257
				// only manage primitive types
1257 1258
				if (f.getType().isAssignableFrom(String.class)) {
1258 1259
					value = this.getStringParameterValue(key);
......
1284 1285
				// SJ: or better, when the abstraction of Property, etc. will be done, we could implement some extensions as in ChartCreator model to other engines, eg. SearchEngine, that will return
1285 1286
				// the right class here
1286 1287
				f.set(this, value);
1287
				
1288

  
1288 1289
				// FIXME: Debug
1289 1290
				Log.finest("TXMResult.autoLoadParametersFromAnnotations(): setting parameter " + key + " = " + value + "\t[" + this.getClass() + "]");
1290
				
1291

  
1291 1292
			}
1292 1293
			catch (Exception e) {
1293 1294
				e.printStackTrace();
......
1295 1296
		}
1296 1297
		return true;
1297 1298
	}
1298
	
1299

  
1299 1300
	/**
1300 1301
	 * Method dedicated to manually create and load non primitive parameters from strings stored in preference nodes in subclasses.
1301 1302
	 * This method is called by the constructor of the class TXMResult.
......
1305 1306
	 * @throws Exception
1306 1307
	 */
1307 1308
	public abstract boolean loadParameters() throws Exception;
1308
	
1309

  
1309 1310
	/**
1310 1311
	 * Initializes the @Parameter class members objects
1311 1312
	 * 
......
1313 1314
	 * @return
1314 1315
	 */
1315 1316
	public abstract boolean setParameters(TXMParameters parameters) throws Exception;
1316
	
1317

  
1317 1318
	/**
1318 1319
	 * Initializes the @Parameter class members objects
1319 1320
	 * 
......
1324 1325
	public final boolean setParameters(HashMap<String, Object> parameters) throws Exception {
1325 1326
		return setParameters(new TXMParameters(parameters));
1326 1327
	}
1327
	
1328

  
1328 1329
	/**
1329 1330
	 * Sets a parameter from its parameter annotation "key".
1330 1331
	 * 
......
1336 1337
	public boolean setParameter(String key, Object value) throws Exception {
1337 1338
		return setParameter(key, value, false);
1338 1339
	}
1339
	
1340

  
1340 1341
	/**
1341 1342
	 * Sets a parameter from its parameter annotation "key".
1342 1343
	 * 
......
1348 1349
	 * @throws Exception
1349 1350
	 */
1350 1351
	public boolean setParameter(String key, Object value, boolean propagateToParent) throws Exception {
1351
		
1352

  
1352 1353
		Field targetField = BeanParameters.getField(this, key);
1353
		
1354

  
1354 1355
		if (targetField != null) {
1355 1356
			targetField.setAccessible(true);
1356 1357
			targetField.set(this, value);
1357
			
1358

  
1358 1359
			// Log
1359 1360
			String message = "TXMResult.setParameter(): " + this.getClass().getSimpleName() + ": setting parameter " + key + " = " + value; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1360 1361
			Log.finest(message);
1361
			
1362

  
1362 1363
		}
1363 1364
		else if (this.parent != null && propagateToParent) {
1364 1365
			this.parent.setParameter(key, value, propagateToParent);
1365 1366
		}
1366
		
1367

  
1367 1368
		return true;
1368 1369
	}
1369
	
1370

  
1370 1371
	/**
1371 1372
	 * Deletes the object from its parent, also deletes the children. The <code>TXMResult.clean()</code> methods of this result and children results are applied before the deletion.
1372 1373
	 * default behavior is not silent
......
1376 1377
	public final boolean delete() {
1377 1378
		return delete(false);
1378 1379
	}
1379
	
1380

  
1380 1381
	/**
1381 1382
	 * Deletes the object from its parent, also deletes the children. The <code>TXMResult.clean()</code> methods of this result and children results are applied before the deletion.
1382 1383
	 * 
......
1384 1385
	 * @return
1385 1386
	 */
1386 1387
	public final boolean delete(boolean silent) {
1387
		
1388

  
1388 1389
		try {
1389 1390
			// FIXME: debug
1390 1391
			// System.err.println("TXMResult.delete()");
1391
			
1392

  
1392 1393
			// START WITH CHILDREN
1393
			
1394

  
1394 1395
			// remove children and clean resources
1395 1396
			while (this.children.size() > 0) {
1396 1397
				TXMResult c = this.children.get(0);
......
1398 1399
				this.children.remove(c); // but should be done already...
1399 1400
			}
1400 1401
			this.children.clear();
1401
			
1402

  
1402 1403
			// THEN FINISH WITH THIS
1403 1404
			// delete the local node
1404 1405
			TXMPreferences.delete(this);
1405
			
1406

  
1406 1407
			// specific cleaning
1407 1408
			this.clean();
1408
			
1409

  
1409 1410
			// log
1410
			if ((this.isVisible()
1411
					&& !silent) || Log.isLoggingFineLevel()) {
1411
			if ((this.isVisible() && !silent) || Log.isLoggingFineLevel()) {
1412 1412
				Log.info(TXMCoreMessages.bind(TXMCoreMessages.info_p0P1Deleted, this.getResultType(), this.getSimpleName()));
1413 1413
			}
1414
			
1414

  
1415 1415
			// removes the parent only after the log since getSimpleName() can use it
1416 1416
			if (this.parent != null) {
1417 1417
				this.parent.removeChild(this);
1418 1418
			}
1419
			
1419

  
1420 1420
			return true;
1421 1421
		}
1422 1422
		catch (Exception e) {
1423 1423
			e.printStackTrace();
1424 1424
		}
1425
		
1425

  
1426 1426
		return false;
1427 1427
	}
1428
	
1428

  
1429 1429
	/**
1430 1430
	 * Deletes all non persistent results and saves the results to persist.
1431 1431
	 */
......
1444 1444
			}
1445 1445
		}
1446 1446
	}
1447
	
1448
	
1447

  
1448

  
1449 1449
	/**
1450 1450
	 * Gets a child specified by its index in the children list.
1451 1451
	 * 
......
1460 1460
			return null;
1461 1461
		}
1462 1462
	}
1463
	
1463

  
1464 1464
	/**
1465 1465
	 * Adds a child result to this result.
1466 1466
	 * If the child already have a parent, the child will be remove from its old parent children.
......
1472 1472
		try {
1473 1473
			// remove from current parent if exists
1474 1474
			child.removeFromParent();
1475
			
1475

  
1476 1476
			child.setParent(this);
1477 1477
			if (!this.children.contains(child)) {
1478 1478
				return this.children.add(child);
......
1484 1484
		}
1485 1485
		return false;
1486 1486
	}
1487
	
1487

  
1488 1488
	/**
1489 1489
	 * Inserts a child result in this result children list at the specified position.
1490 1490
	 * 
......
1504 1504
		}
1505 1505
		return false;
1506 1506
	}
1507
	
1507

  
1508 1508
	/**
1509 1509
	 * Removes the specified child result.
1510 1510
	 * 
......
1520 1520
		}
1521 1521
		return false;
1522 1522
	}
1523
	
1523

  
1524 1524
	/**
1525 1525
	 * Removes the child result specified by its index.
1526 1526
	 * 
......
1532 1532
			return this.removeChild(this.children.get(index));
1533 1533
		}
1534 1534
		catch (Exception e) {
1535
			
1535

  
1536 1536
		}
1537 1537
		return false;
1538 1538
	}
1539
	
1539

  
1540 1540
	/**
1541 1541
	 * Removes the result from its parent: call the parent's removeResult method.
1542 1542
	 * 
......
1547 1547
			return this.parent.removeChild(this);
1548 1548
		}
1549 1549
		catch (Exception e) {
1550
			
1550

  
1551 1551
		}
1552 1552
		return false;
1553 1553
	}
1554
	
1555
	
1554

  
1555

  
1556 1556
	/**
1557 1557
	 * Checks if the result has at least one child visible.
1558 1558
	 * 
......
1561 1561
	public boolean hasVisibleChild() {
1562 1562
		return !this.getChildren(true).isEmpty();
1563 1563
	}
1564
	
1565
	
1564

  
1565

  
1566 1566
	/**
1567 1567
	 * Gets all the children results (not a clone).
1568 1568
	 * 
......
1571 1571
	public List<TXMResult> getChildren() {
1572 1572
		return this.children;
1573 1573
	}
1574
	
1574

  
1575 1575
	/**
1576 1576
	 * Gets the children results.
1577 1577
	 * 
......
1580 1580
	public List<TXMResult> getChildren(boolean onlyVisible) {
1581 1581
		return this.getChildren(null, onlyVisible);
1582 1582
	}
1583
	
1583

  
1584 1584
	/**
1585 1585
	 * Gets the children results specified by their class.
1586 1586
	 * 
......
1590 1590
	public <T extends TXMResult> List<T> getChildren(Class<T> clazz) {
1591 1591
		return TXMResult.getNodes(this.children, clazz, false);
1592 1592
	}
1593
	
1593

  
1594 1594
	/**
1595 1595
	 * Gets the children results specified by their class.
1596 1596
	 * 
......
1600 1600
	public <T extends TXMResult> List<T> getChildren(Class<T> clazz, boolean onlyVisible) {
1601 1601
		return TXMResult.getNodes(this.children, clazz, onlyVisible);
1602 1602
	}
1603
	
1603

  
1604 1604
	/**
1605 1605
	 * Gets the first child result specified by its class.
1606 1606
	 * 
......
1616 1616
			return null;
1617 1617
		}
1618 1618
	}
1619
	
1619

  
1620 1620
	/**
1621 1621
	 * Deletes all the children
1622 1622
	 * 
......
1625 1625
	public void deleteChildren() {
1626 1626
		deleteChildren(null);
1627 1627
	}
1628
	
1628

  
1629 1629
	/**
1630 1630
	 * Deletes the children of the specified class.
1631 1631
	 * 
......
1638 1638
			children.get(i).delete();
1639 1639
		}
1640 1640
	}
1641
	
1642
	
1643
	
1641

  
1642

  
1643

  
1644 1644
	/**
1645 1645
	 * Gets the children of all the branch of the specified node in a flat list according to their specified class.
1646 1646
	 * 
......
1650 1650
	 */
1651 1651
	@SuppressWarnings("unchecked")
1652 1652
	protected static <T extends TXMResult> List<T> getDeepChildren(TXMResult parent, Class<T> clazz) {
1653
		
1653

  
1654 1654
		List<T> results = new ArrayList<>();
1655 1655
		List<TXMResult> children = parent.getChildren();
1656
		
1656

  
1657 1657
		for (int i = 0; i < children.size(); i++) {
1658 1658
			TXMResult child = children.get(i);
1659 1659
			if (clazz == null || child.getClass().isAssignableFrom(clazz)) {
......
1663 1663
		}
1664 1664
		return results;
1665 1665
	}
1666
	
1666

  
1667 1667
	/**
1668 1668
	 * Gets the children of all the branch in a flat list.
1669 1669
	 * 
......
1672 1672
	public <T extends TXMResult> List<T> getDeepChildren(Class<T> clazz) {
1673 1673
		return TXMResult.getDeepChildren(this, clazz);
1674 1674
	}
1675
	
1675

  
1676 1676
	/**
1677 1677
	 * Gets the children of all the branch in a flat list.
1678 1678
	 * 
......
1681 1681
	public static List<TXMResult> getDeepChildren(TXMResult result) {
1682 1682
		return TXMResult.getDeepChildren(result, null);
1683 1683
	}
1684
	
1684

  
1685 1685
	/**
1686 1686
	 * Gets the children of all the branch in a flat list.
1687 1687
	 * 
......
1690 1690
	public List<TXMResult> getDeepChildren() {
1691 1691
		return TXMResult.getDeepChildren(this);
1692 1692
	}
1693
	
1693

  
1694 1694
	/**
1695 1695
	 * Gets the TXMResult specified by its node path (UUID) in the children of the specified root result.
1696 1696
	 * Returns the root itself if the specified node path is equal to the root node path.
......
1700 1700
	 * @return
... Ce différentiel a été tronqué car il excède la taille maximale pouvant être affichée.

Formats disponibles : Unified diff