001 package jigcell.compare.impl;
002
003 import EDU.oswego.cs.dl.util.concurrent.ReentrantWriterPreferenceReadWriteLock;
004 import EDU.oswego.cs.dl.util.concurrent.Sync;
005 import bsh.Capabilities;
006 import java.beans.Encoder;
007 import java.beans.PersistenceDelegate;
008 import java.beans.PropertyChangeEvent;
009 import java.beans.PropertyChangeListener;
010 import java.beans.PropertyChangeSupport;
011 import java.io.IOException;
012 import java.io.InputStream;
013 import java.security.Permission;
014 import java.text.MessageFormat;
015 import java.util.ArrayList;
016 import java.util.Collections;
017 import java.util.HashMap;
018 import java.util.Iterator;
019 import java.util.List;
020 import java.util.Map;
021 import java.util.ResourceBundle;
022 import java.util.Set;
023 import java.util.logging.ConsoleHandler;
024 import java.util.logging.FileHandler;
025 import java.util.logging.Level;
026 import java.util.logging.Logger;
027 import javax.swing.JFrame;
028 import jigcell.compare.IDataSource;
029 import jigcell.compare.ITab;
030
031 /**
032 * The Comparator backend and some support for a shell servicing the Comparator frontend.
033 *
034 * <p>
035 * This code is licensed under the DARPA BioCOMP Open Source License. See LICENSE for more details.
036 * </p>
037 *
038 * @author Nicholas Allen
039 */
040
041 public class Compare {
042
043 /**
044 * Property key for class names
045 */
046
047 public final static String CONFIG_CLASSNAME = "Compare.class";
048
049 /**
050 * Property key for data source prototypes
051 */
052
053 public final static String CONFIG_DATASOURCE = "Compare.dataSources";
054
055 /**
056 * Parameters if none specified
057 */
058
059 public final static String DEFAULT_CONFIG = "compare.config";
060
061 /**
062 * Dialog title for error dialogs
063 */
064
065 public final static String MESSAGE_ERROR;
066
067 /**
068 * Dialog title for warning dialogs
069 */
070
071 public final static String MESSAGE_WARNING;
072
073 /**
074 * Property name used when the central configuration repository has changed
075 */
076
077 public final static String PROPERTY_CONFIG_EDIT = "compare_config_edited";
078
079 /**
080 * Resource key for the central configuration repository
081 */
082
083 public final static String RESOURCE_CONFIG = "compare_config";
084
085 /**
086 * Resource key for the central data source maintainer
087 */
088
089 public final static String RESOURCE_DATAMANAGER = "compare_datamanager";
090
091 /**
092 * Resource key for tabs added to the Comparator
093 */
094
095 public final static String RESOURCE_TABS = "compare_tabs";
096
097 /**
098 * Command line parameter to enable exception debugging
099 */
100
101 protected final static String PARAMETER_DEBUG_EXCEPTIONS = "debug_exceptions";
102
103 /**
104 * Configuration property to enable exception debugging
105 */
106
107 protected final static String PROPERTY_DEBUG_EXCEPTIONS = "Compare.debugExceptions";
108
109 /**
110 * Whether to provide debugging information for exceptions
111 */
112
113 private static volatile boolean DEBUG_EXCEPTIONS;
114
115 /**
116 * Method signature of data sources
117 */
118
119 private final static Class SIGNATURE_DATASOURCE [] = new Class [] {};
120
121 /**
122 * Maximum number of files to log
123 */
124
125 private final static int LOG_COUNT = 2;
126
127 /**
128 * Maximum number of bytes to log
129 */
130
131 private final static int LOG_LIMIT = 1048576;
132
133 /**
134 * Logger to output to
135 */
136
137 private final static Logger logger = Logger.getAnonymousLogger ();
138
139 /**
140 * Lock for controlling resource bundle manipulation
141 */
142
143 private final static ReentrantWriterPreferenceReadWriteLock BUNDLE_LOCK = new ReentrantWriterPreferenceReadWriteLock ();
144
145 /**
146 * Resource bundle for string translation
147 */
148
149 private static ResourceBundle resourceBundle;
150
151 /**
152 * Logging filename pattern
153 */
154
155 private final static String LOG_PATTERN = "compare.log";
156
157 /**
158 * Formatter for controlling how XML persistence behaves
159 */
160
161 private final static XMLFormatter formatter = new XMLFormatter ();
162
163 /**
164 * Security manager for Comparator
165 */
166
167 protected CompareSecurityManager securityManager;
168
169 /**
170 * Data source for the configuration file
171 */
172
173 protected FileDataSource configSource;
174
175 /**
176 * Standard configuration markers for this component
177 */
178
179 protected List configMarkers;
180
181 /**
182 * Lock for controlling tab manipulation
183 */
184
185 protected final Object TAB_LOCK = new Object ();
186
187 /**
188 * Collection of resources maintained for system
189 */
190
191 private final Map resources = new HashMap ();
192
193 /**
194 * Support for handling property change events
195 */
196
197 private PropertyChangeSupport propertySupport;
198
199 /**
200 * Lock for controlling resource manipulation
201 */
202
203 private final ReentrantWriterPreferenceReadWriteLock RESOURCE_LOCK = new ReentrantWriterPreferenceReadWriteLock ();
204
205 /**
206 * Information about a Comparator tab.
207 */
208
209 public static class TabInfo {
210
211 /**
212 * Tab
213 */
214
215 private ITab tab;
216
217 /**
218 * Creates a new tab description.
219 *
220 * @param tab Tab
221 */
222
223 public TabInfo (ITab tab) {
224 this.tab = tab;
225 }
226
227 /**
228 * Not allowed.
229 */
230
231 private TabInfo () {}
232
233 /**
234 * The tab display name.
235 */
236
237 public String getName () {
238 return tab.getName ();
239 }
240
241 /**
242 * The tab.
243 */
244
245 public ITab getTab () {
246 return tab;
247 }
248 }
249
250 /**
251 * Controls termination of the Comparator to prevent badly behaving tabs from causing problems.
252 */
253
254 protected class CompareSecurityManager extends SecurityManager {
255
256 /**
257 * Whether a shutdown should be allowed to proceed.
258 */
259
260 protected boolean allowShutdown;
261
262 /**
263 * Creates a new security manager.
264 */
265
266 protected CompareSecurityManager () {
267 super ();
268 }
269
270 public void checkExit (int status) {
271 if (allowShutdown)
272 return;
273 exit (status);
274 throw new SecurityException ();
275 }
276
277 public void checkPermission (Permission permission) {}
278
279 public void checkPermission (Permission permission, Object context) {}
280
281 /**
282 * Shuts down the virtual machine.
283 */
284
285 public void terminate (int status) {
286 allowShutdown = true;
287 System.exit (status);
288 }
289 }
290
291 static {
292 DEBUG_EXCEPTIONS = Boolean.getBoolean (PARAMETER_DEBUG_EXCEPTIONS);
293 try {
294 logger.addHandler (new FileHandler (LOG_PATTERN, LOG_LIMIT, LOG_COUNT, true));
295 } catch (Exception e) {
296 logger.addHandler (new ConsoleHandler ());
297 assertion ("Unable to open log file.", e);
298 }
299 logger.setUseParentHandlers (false);
300 try {
301 addResourceBundle ("/jigcell/compare/impl/resource/core.properties");
302 } catch (Exception e) {
303 assertion ("Unable to load core resource bundle.", e);
304 }
305 MESSAGE_ERROR = getString ("Compare.titleError");
306 MESSAGE_WARNING = getString ("Compare.titleWarning");
307 Capabilities.setAccessibility (true);
308 }
309
310 /**
311 * Acquires the lock of a Sync object.
312 *
313 * @param sync Sync
314 */
315
316 public final static void acquireSync (Sync sync) {
317 while (true)
318 try {
319 sync.acquire ();
320 break;
321 } catch (InterruptedException e) {
322 assertion (getString ("Compare.syncAcquire"), e);
323 }
324 }
325
326 /**
327 * @see jigcell.compare.impl.XMLFormatter#addDelegate(java.lang.Class,java.beans.PersistenceDelegate)
328 */
329
330 public final static void addDelegate (Class clazz, PersistenceDelegate delegate) {
331 formatter.addDelegate (clazz, delegate);
332 }
333
334 /**
335 * Loads a table of resource strings for the current locale.
336 *
337 * @param resourceName Name of resource to bundle to load
338 */
339
340 public final static void addResourceBundle (String resourceName) throws IOException {
341 if (resourceName == null)
342 throw new IllegalArgumentException ();
343 InputStream resourceStream = Compare.class.getResourceAsStream (resourceName);
344 if (resourceStream == null)
345 throw new IOException (resourceBundle == null ? "Unable to load resource bundle." : getString ("Compare.missingResourceError"));
346 Sync sync = BUNDLE_LOCK.writeLock ();
347 acquireSync (sync);
348 try {
349 resourceBundle = new LinkedPropertyResourceBundle (resourceStream, resourceBundle);
350 } finally {
351 sync.release ();
352 }
353 }
354
355 /**
356 * @see jigcell.compare.impl.XMLFormatter#addTransient(java.lang.Class,java.lang.String)
357 */
358
359 public final static void addTransient (Class clazz, String field) {
360 formatter.addTransient (clazz, field);
361 }
362
363 /**
364 * Logs an exceptional condition and asserts if debugging is enabled.
365 *
366 * @param message Exception message
367 */
368
369 public final static void assertion (String message) {
370 assertion (message, null);
371 }
372
373 /**
374 * Logs an exceptional condition and asserts if debugging is enabled.
375 *
376 * @param message Exception message
377 * @param t Throwable
378 */
379
380 public final static void assertion (String message, Throwable t) {
381 for (Throwable cause = t; cause != null; cause = t.getCause ())
382 t = cause;
383 warning (message, t);
384 assert !getDebugExceptions () : t;
385 }
386
387 /**
388 * Acquires the lock of a Sync object or returns false if this operation would block.
389 *
390 * @param sync Sync
391 */
392
393 public final static boolean attemptSync (Sync sync) {
394 try {
395 return sync.attempt (0);
396 } catch (InterruptedException e) {
397 assertion (getString ("Compare.syncAttempt"), e);
398 }
399 return false;
400 }
401
402 /**
403 * Whether to display exception debugging information
404 */
405
406 public final static boolean getDebugExceptions () {
407 return DEBUG_EXCEPTIONS;
408 }
409
410 /**
411 * Looks up a string in the table of resources and applies a message format to it.
412 *
413 * @param key Resource key
414 * @param object Format object
415 */
416
417 public final static String formatString (String key, Object object) {
418 return formatString (key, new Object [] {object});
419 }
420
421 /**
422 * Looks up a string in the table of resources and applies a message format to it.
423 *
424 * @param key Resource key
425 * @param objects Format objects
426 */
427
428 public final static String formatString (String key, Object objects []) {
429 return MessageFormat.format (getString (key), objects);
430 }
431
432 /**
433 * Looks up a string in the table of resources.
434 *
435 * @param key Resource key
436 */
437
438 public final static String getString (String key) {
439 Sync sync = BUNDLE_LOCK.readLock ();
440 acquireSync (sync);
441 try {
442 if (resourceBundle == null)
443 throw new IllegalStateException ("No resource bundle has been loaded.");
444 return resourceBundle.getString (key);
445 } finally {
446 sync.release ();
447 }
448 }
449
450 /**
451 * Starts a new headless Comparator.
452 *
453 * @param args Program arguments
454 */
455
456 public static void main (String args []) {
457 new Compare (args == null || args.length == 0 ? DEFAULT_CONFIG : args [0]);
458 }
459
460 /**
461 * Logs a message.
462 *
463 * @param message Message
464 */
465
466 public final static void message (String message) {
467 logger.log (Level.INFO, message);
468 }
469
470 /**
471 * Sets whether to display exception debugging information
472 *
473 * @param debug Whether to display exception debugging information
474 */
475
476 public final static void setDebugExceptions (boolean debug) {
477 DEBUG_EXCEPTIONS = debug;
478 }
479
480 /**
481 * Converts a throwable from a potentially checked exception type to an unchecked exception type and throws it.
482 *
483 * @param t Throwable
484 */
485
486 public final static void throwUncheckedException (Throwable t) {
487 if (t instanceof Error)
488 throw (Error) t;
489 if (t instanceof RuntimeException)
490 throw (RuntimeException) t;
491 throw new RuntimeException (t.getMessage (), t);
492 }
493
494 /**
495 * @see jigcell.compare.impl.XMLFormatter#updateEncoder(java.beans.Encoder)
496 */
497
498 public final static void updateEncoder (Encoder encoder) {
499 formatter.updateEncoder (encoder);
500 }
501
502 /**
503 * Logs an exceptional condition.
504 *
505 * @param message Exception message
506 */
507
508 public final static void warning (String message) {
509 warning (message, null);
510 }
511
512 /**
513 * Logs an exceptional condition.
514 *
515 * @param message Exception message
516 * @param t Throwable
517 */
518
519 public final static void warning (String message, Throwable t) {
520 for (Throwable cause = t; cause != null; cause = t.getCause ())
521 t = cause;
522 logger.log (Level.WARNING, message, t);
523 }
524
525 /**
526 * Creates a new headless Comparator.
527 *
528 * @param configFile Name of configuration file
529 */
530
531 public Compare (String configFile) {
532 try {
533 Thread.currentThread ().setContextClassLoader (Compare.class.getClassLoader ());
534 } catch (Exception e) {
535 shellHandleException (MESSAGE_WARNING, getString ("Compare.startupLoaderError"));
536 }
537 try {
538 if (System.getSecurityManager () == null) {
539 securityManager = new CompareSecurityManager ();
540 System.setSecurityManager (securityManager);
541 }
542 } catch (SecurityException e) {
543 shellHandleException (MESSAGE_WARNING, getString ("Compare.startupSecurityError"));
544 }
545 if (configFile == null) {
546 String errorMessage = getString ("Compare.configNoFileError");
547 shellHandleException (MESSAGE_ERROR, errorMessage);
548 throw new IllegalArgumentException (errorMessage);
549 }
550 shellStartHook ();
551 propertySupport = new PropertyChangeSupport (this);
552 propertySupport.addPropertyChangeListener (ProxyBuilder.proxyPropertyChangeListener (this, "propertyChange"));
553 configSource = new XMLFileDataSource (configFile);
554 configSource.setExceptionListenerOption (new ExceptionRecorder (this, getString ("Compare.configFormatError"), false));
555 Config config;
556 try {
557 config = (Config) configSource.read ();
558 } catch (Exception e) {
559 config = new Config ();
560 shellHandleException (MESSAGE_ERROR, getString ("Compare.configLoadError"), e);
561 } finally {
562 configSource.finish ();
563 }
564 configMarkers = Config.createStandardMarkers (this);
565 setConfig (config);
566 shellStableHook ();
567 try {
568 DEBUG_EXCEPTIONS |= Config.convertToBoolean (config.findValue (configMarkers, PROPERTY_DEBUG_EXCEPTIONS, false, false, false));
569 } catch (Exception e) {
570 shellHandleException (MESSAGE_WARNING, getString ("Compare.debugReadError"));
571 }
572 DataManager dataManager = createDataManager ();
573 setDataManager (dataManager == null ? new DataManager () : dataManager);
574 }
575
576 /**
577 * Adds a PropertyChangeListener to the listener list.
578 *
579 * @param listener Listener
580 */
581
582 public void addPropertyChangeListener (PropertyChangeListener listener) {
583 propertySupport.addPropertyChangeListener (listener);
584 }
585
586 /**
587 * Adds a PropertyChangeListener for a specific property.
588 *
589 * @param property Property
590 * @param listener Listener
591 */
592
593 public void addPropertyChangeListener (String property, PropertyChangeListener listener) {
594 propertySupport.addPropertyChangeListener (property, listener);
595 }
596
597 /**
598 * Adds a mapping between the give name and resource.
599 *
600 * @return Whether the resource could be added
601 */
602
603 public boolean addResource (String name, Object resource) {
604 if (name == null || resource == null)
605 return false;
606 Sync sync = RESOURCE_LOCK.writeLock ();
607 acquireSync (sync);
608 try {
609 if (resources.containsKey (name))
610 return false;
611 resources.put (name, resource);
612 return true;
613 } finally {
614 sync.release ();
615 }
616 }
617
618 /**
619 * Adds a tab to the main Comparator display.
620 *
621 * @param component Contents of the tab
622 */
623
624 public TabInfo addTab (ITab component) {
625 TabInfo info = new TabInfo (component);
626 synchronized (TAB_LOCK) {
627 updateResourceMap (RESOURCE_TABS, component, info);
628 }
629 return info;
630 }
631
632 /**
633 * A tab registered with the Comparator of the specified type or null if no such tab exists.
634 *
635 * @param clazz Tab class
636 */
637
638 public ITab findTabByClass (Class clazz) {
639 if (clazz == null)
640 return null;
641 for (Iterator iterator = getTabMap ().keySet ().iterator (); iterator.hasNext (); ) {
642 ITab tab = (ITab) iterator.next ();
643 if (clazz.isAssignableFrom (tab.getClass ()))
644 return tab;
645 }
646 return null;
647 }
648
649 /**
650 * A tab registered with the Comparator of the specified type or null if no such tab exists.
651 *
652 * @param clazz Tab class
653 */
654
655 public ITab findTabByClassExact (Class clazz) {
656 if (clazz == null)
657 return null;
658 for (Iterator iterator = getTabMap ().keySet ().iterator (); iterator.hasNext (); ) {
659 ITab tab = (ITab) iterator.next ();
660 if (clazz == tab.getClass ())
661 return tab;
662 }
663 return null;
664 }
665
666 /**
667 * A tab registered with the Comparator of the specified name or null if no such tab exists.
668 *
669 * @param name Tab name
670 */
671
672 public ITab findTabByName (String name) {
673 if (name == null)
674 return null;
675 for (Iterator iterator = getTabMap ().entrySet ().iterator (); iterator.hasNext (); ) {
676 Map.Entry entry = (Map.Entry) iterator.next ();
677 if (name.equals (((TabInfo) entry.getValue ()).getName ()))
678 return (ITab) entry.getKey ();
679 }
680 return null;
681 }
682
683 /**
684 * Fires a property change message about a named resource.
685 *
686 * @param name Resource name
687 */
688
689 public void firePropertyChange (String name) {
690 if (name == null)
691 throw new IllegalArgumentException ();
692 firePropertyChange (name, null, getResource (name));
693 }
694
695 /**
696 * Fires a property change message about a named resource.
697 *
698 * @param name Resource name
699 * @param oldValue Old resource value
700 * @param newValue New resource value
701 */
702
703 public void firePropertyChange (String name, Object oldValue, Object newValue) {
704 propertySupport.firePropertyChange (name, oldValue, newValue);
705 }
706
707 /**
708 * The configuration resource.
709 */
710
711 public Config getConfig () {
712 return (Config) getResource (RESOURCE_CONFIG);
713 }
714
715 /**
716 * The data manager resource.
717 */
718
719 public DataManager getDataManager () {
720 return (DataManager) getResource (RESOURCE_DATAMANAGER);
721 }
722
723 /**
724 * The frontend shell display frame. Returns null if this is a headless Comparator.
725 */
726
727 public JFrame getDisplayFrame () {
728 return null;
729 }
730
731 /**
732 * An array of all the PropertyChangeListeners added to this Compare with addPropertyChangeListener.
733 */
734
735 public PropertyChangeListener [] getPropertyChangeListeners () {
736 return propertySupport.getPropertyChangeListeners ();
737 }
738
739 /**
740 * An array of all the listeners which have been associated with the named property.
741 *
742 * @param property Property
743 */
744
745 public PropertyChangeListener [] getPropertyChangeListeners (String property) {
746 return propertySupport.getPropertyChangeListeners (property);
747 }
748
749 /**
750 * A named resource.
751 *
752 * @param name Resource name
753 */
754
755 public Object getResource (String name) {
756 if (name == null)
757 return null;
758 Sync sync = RESOURCE_LOCK.readLock ();
759 acquireSync (sync);
760 try {
761 return resources.get (name);
762 } finally {
763 sync.release ();
764 }
765 }
766
767 /**
768 * A named resource known to be a map. Creates a map if none exists so that future modifications of the named resource map are properly
769 * updated in existing instances.
770 *
771 * @param name Resource name
772 */
773
774 public Map getResourceMap (String name) {
775 if (name == null)
776 throw new IllegalArgumentException ();
777 Sync sync = RESOURCE_LOCK.writeLock ();
778 acquireSync (sync);
779 try {
780 Map map = (Map) getResource (name);
781 if (map == null) {
782 map = Collections.synchronizedMap (new HashMap ());
783 addResource (name, map);
784 }
785 return map;
786 } finally {
787 sync.release ();
788 }
789 }
790
791 /**
792 * The currently used resource names.
793 */
794
795 public Set getResourceNames () {
796 Sync sync = RESOURCE_LOCK.readLock ();
797 acquireSync (sync);
798 try {
799 return resources.keySet ();
800 } finally {
801 sync.release ();
802 }
803 }
804
805 /**
806 * The tab resource map.
807 */
808
809 public Map getTabMap () {
810 return getResourceMap (RESOURCE_TABS);
811 }
812
813 /**
814 * All of the tabs currently registered with the Comparator.
815 */
816
817 public ITab [] getTabs () {
818 return (ITab []) getTabMap ().keySet ().toArray (new ITab [0]);
819 }
820
821 /**
822 * Removes a PropertyChangeListener from the listener list.
823 *
824 * @param listener Listener
825 */
826
827 public void removePropertyChangeListener (PropertyChangeListener listener) {
828 propertySupport.removePropertyChangeListener (listener);
829 }
830
831 /**
832 * Removes a PropertyChangeListener for a specific property.
833 *
834 * @param property Property
835 * @param listener Listener
836 */
837
838 public void removePropertyChangeListener (String property, PropertyChangeListener listener) {
839 propertySupport.removePropertyChangeListener (property, listener);
840 }
841
842 /**
843 * Deletes a named resource.
844 *
845 * @param name Resource name
846 */
847
848 public boolean removeResource (String name) {
849 if (name == null)
850 return false;
851 Sync sync = RESOURCE_LOCK.writeLock ();
852 acquireSync (sync);
853 try {
854 return resources.remove (name) != null;
855 } finally {
856 sync.release ();
857 }
858 }
859
860 /**
861 * Adds a mapping between the give name and resource replacing any existing mapping. This method should be used with caution. Editing a
862 * shared instance is preferrable to replacing instances.
863 *
864 * @param name Resource name
865 * @param resource Resource
866 */
867
868 public boolean replaceResource (String name, Object resource) {
869 if (name == null || resource == null)
870 return false;
871 Sync sync = RESOURCE_LOCK.writeLock ();
872 acquireSync (sync);
873 try {
874 return resources.put (name, resource) == null;
875 } finally {
876 sync.release ();
877 }
878 }
879
880 /**
881 * Compiles the contents of a resource catalog. A resource catalog is a resource map whose values are all lists.
882 *
883 * @param name Resource name
884 */
885
886 public List scanResourceCatalog (String name) {
887 if (name == null)
888 throw new IllegalArgumentException ();
889 List resource = new ArrayList ();
890 Sync sync = RESOURCE_LOCK.readLock ();
891 acquireSync (sync);
892 try {
893 Map map = getResourceMap (name);
894 synchronized (map) {
895 for (Iterator iterator = map.values ().iterator (); iterator.hasNext (); ) {
896 Object entry = iterator.next ();
897 if (entry instanceof List)
898 resource.addAll ((List) entry);
899 else
900 assertion (getString ("Compare.resourceScanError"));
901 }
902 }
903 } finally {
904 sync.release ();
905 }
906 return resource;
907 }
908
909 /**
910 * Compiles the contents of a resource map.
911 *
912 * @param name Resource name
913 */
914
915 public List scanResourceMap (String name) {
916 if (name == null)
917 throw new IllegalArgumentException ();
918 List resource = new ArrayList ();
919 Map map = getResourceMap (name);
920 resource.addAll (map.values ());
921 return resource;
922 }
923
924 /**
925 * Compiles the contents of all resources.
926 */
927
928 public List scanResources () {
929 List resource = new ArrayList ();
930 Sync sync = RESOURCE_LOCK.readLock ();
931 acquireSync (sync);
932 try {
933 resource.addAll (resources.values ());
934 } finally {
935 sync.release ();
936 }
937 return resource;
938 }
939
940 /**
941 * Handle an exception in a Comparator component.
942 *
943 * @param type Exception type
944 * @param message Display message
945 */
946
947 public void shellHandleException (String type, Object message) {
948 shellHandleException (type, message, null);
949 }
950
951 /**
952 * Handle an exception in a Comparator component.
953 *
954 * @param type Exception type
955 * @param message Display message
956 * @param t Throwable
957 */
958
959 public void shellHandleException (String type, Object message, Throwable t) {
960 String text = String.valueOf (message);
961 System.err.println ("*** " + type + "\n" + text);
962 assertion (text, t);
963 }
964
965 /**
966 * Handle an informational message in a Comparator component.
967 *
968 * @param type Message type
969 * @param message Display message
970 */
971
972 public void shellHandleMessage (String type, Object message) {
973 String text = String.valueOf (message);
974 System.out.println (text);
975 message (text);
976 }
977
978 /**
979 * Changes a mapping in a named resource known to be a map.
980 *
981 * @param name Resource name
982 * @param key Mapping key
983 * @param value Mapping value
984 */
985
986 public void updateResourceMap (String name, Object key, Object value) {
987 if (name == null || key == null)
988 throw new IllegalArgumentException ();
989 Sync sync = RESOURCE_LOCK.writeLock ();
990 acquireSync (sync);
991 try {
992 Map map = getResourceMap (name);
993 map.put (key, value);
994 } finally {
995 sync.release ();
996 }
997 }
998
999 /**
1000 * Builds the repository of external data sources.
1001 */
1002
1003 protected DataManager createDataManager () {
1004 DataManager dataManager = new DataManager ();
1005 Config config = getConfig ();
1006 try {
1007 Object param [] = new Object [0];
1008 for (Iterator iterator = Config.convertToList (config.findValue (configMarkers, CONFIG_DATASOURCE, false, false, false)).iterator ();
1009 iterator.hasNext (); )
1010 try {
1011 dataManager.addPrototypeSource (
1012 (IDataSource) Class.forName ((String) iterator.next ()).getDeclaredConstructor (SIGNATURE_DATASOURCE).newInstance (param));
1013 } catch (Exception e) {
1014 shellHandleException (MESSAGE_WARNING, getString ("Compare.dataLoadError"), e);
1015 }
1016 } catch (Exception e) {
1017 shellHandleException (MESSAGE_WARNING, getString ("Compare.dataListError"));
1018 }
1019 return dataManager;
1020 }
1021
1022 /**
1023 * Exits from the Comparator.
1024 *
1025 * @param status Status code
1026 */
1027
1028 protected void exit (int status) {
1029 if (securityManager == null)
1030 System.exit (status);
1031 securityManager.terminate (status);
1032 }
1033
1034 protected void propertyChange (PropertyChangeEvent e) {
1035 if (!PROPERTY_CONFIG_EDIT.equals (e.getPropertyName ()))
1036 return;
1037 Config config = getConfig ();
1038 Sync sync = config.getLock ().readLock ();
1039 acquireSync (sync);
1040 try {
1041 try {
1042 configSource.write (getConfig ());
1043 } catch (Exception _e) {
1044 assertion (getString ("Compare.configSaveError"), _e);
1045 } finally {
1046 configSource.finish ();
1047 }
1048 } finally {
1049 sync.release ();
1050 }
1051 }
1052
1053 /**
1054 * Sets the configuration resource.
1055 *
1056 * @param config Configuration
1057 */
1058
1059 protected void setConfig (Config config) {
1060 addResource (RESOURCE_CONFIG, config);
1061 }
1062
1063 /**
1064 * Sets the data manager resource.
1065 *
1066 * @param manager Data manager
1067 */
1068
1069 protected void setDataManager (DataManager manager) {
1070 addResource (RESOURCE_DATAMANAGER, manager);
1071 }
1072
1073 /**
1074 * Stable hook for frontend shell. The system configuration is guaranteed to have been loaded but no processing has been done.
1075 */
1076
1077 protected void shellStableHook () {
1078 message (getString ("Compare.shellStable"));
1079 }
1080
1081 /**
1082 * Start hook for frontend shell. No initialization is guaranteed to have taken place at this point.
1083 */
1084
1085 protected void shellStartHook () {
1086 message (getString ("Compare.shellStart"));
1087 }
1088 }