/** * */ package photoSpreadUtilities; import java.rmi.AlreadyBoundException; import java.rmi.NotBoundException; import java.util.HashMap; import javax.swing.JFrame; /** * @author paepcke * * Manages tracking of user-initiated modifications to * the system's state. Used to report when a registered * client, such as a sheet or a metadata editor turns * dirty, so that the client's window title bar can have a star * placed on it. * * In future we might add undo here. */ public class ChangeManager { private static final String DIRTY_MARKER = "* "; // private static final String DIRTY_MARKER = "<html><style>dirtMarker {color: red}</style><dirtMarker>*</dirtMarker> "; private static HashMap<String, ChangeClient> _clients = new HashMap<String, ChangeClient>(); /**************************************************** * Constructor(s) *****************************************************/ /** * This class is intended to be used as static only. So we * disallow instantiation. (For private exceptions see below * in body of registerClient()) * @throws IllegalAccessException */ public ChangeManager () throws IllegalAccessException { throw new IllegalAccessException("ChangeManager is only used in static mode."); } // Sneaky way to allow creation of an instance from // within this ChangeManager class // only. See body of registerClient() for details private ChangeManager (String codeWord) { } /**************************************************** * Private (Innter) Classes *****************************************************/ class ChangeClient { private String _clientName; private JFrame _clientWindowFrame; private boolean _dirty = false; protected ChangeClient(String clientName, JFrame clientWindowFrame) { _clientName = clientName; _clientWindowFrame = clientWindowFrame; } /** * * @return Name of this ChangeClient instance */ protected String getName() { return _clientName; } /** * Mark this client either dirty or clean. * @param isDirty Boolean indicating whether this client is now dirty or clean. */ protected void isDirty(boolean isDirty) { _dirty = isDirty; } protected boolean isDirty() { return _dirty; } protected JFrame getWindowFrame () { return _clientWindowFrame; } } // end inner class ChangeClient /**************************************************** * Methods *****************************************************/ /** * Clients who require change service need to register * themselves via this method. * * @param clientName Desired name for this client. Must be unique among * all clients who are served by this ChangeManager, but is otherwise arbitrary * * @param clientWindow JFrame that contains the client * @return True if client registered as a new client. False if client was * already registered. * @throws AlreadyBoundException Thrown if another client is already registered * under the provided name. */ public static boolean registerClient (String clientName, JFrame clientWindow) throws AlreadyBoundException { if (_clients.containsKey (clientName)) if (_clients.get(clientName).getName().equals(clientName)) // Same client registered itself already return false; else throw new AlreadyBoundException ("A different change client is already registered under the name '" + clientName + "'"); // We now want to create an instance of the inner class ChangeClient. // But we are only allowed to do this using an *instance* of the // main, outer classe ChangeManager. So we create such an instance // for the sole purpose of being allowed to create the client instance: ChangeManager tempCMInstance = new ChangeManager("Secret temp instance"); _clients.put(clientName, tempCMInstance.new ChangeClient(clientName, clientWindow)); return true; } /** * Unregisters change client. It is legal and harmless to * call this method even if the caller never registered. * @param clientName Name under which calling client is registered * @return True if the unregistering client was actually * registered, False otherwise. */ public static boolean unregisterClient (String clientName) { ChangeClient client = _clients.get(clientName); if (client == null) return false; else { _clients.remove(client); return true; } } /** * Ask ChangeManager to mark the calling client as dirty. * Apart from noting this fact, the ChangeManager visually * marks the client's window title bar. * * @param clientName Name of calling client. * @return Returns true if marking was successful. Else returns false. * Failure happens, for example, if the provided name is not that * of a registered client. */ public static boolean markDirty (String clientName) { ChangeClient theClient; try { theClient = getClientSafely(clientName); } catch (NotBoundException e) {return false;} // This client already marked dirty? if (theClient.isDirty()) return true; theClient.isDirty(true); String currWindowTitle = theClient.getWindowFrame().getTitle(); theClient.getWindowFrame().setTitle(DIRTY_MARKER + currWindowTitle); return true; } /** * Ask ChangeManager to mark the calling client as clean. * Apart from noting this fact, the ChangeManager visually * removes any dirty-mark from the client's window title. * * @param clientName Name of calling client. * @return Returns true if marking was successful. Else returns false. * Failure happens, for example, if the provided name is not that * of a registered client. */ public static boolean markClean(String clientName) { ChangeClient theClient; try { theClient = getClientSafely(clientName); } catch (NotBoundException e) {return false;} // This client already marked clean? if (!theClient.isDirty()) return true; theClient.isDirty(false); String currWindowTitle = theClient.getWindowFrame().getTitle(); theClient.getWindowFrame().setTitle(currWindowTitle.substring(DIRTY_MARKER.length())); return true; } /** * Find out whether client with given name is dirty. * @param clientName * @return True if client was marked dirty, else False * @throws NotBoundException */ public static boolean isDirty (String clientName) throws NotBoundException { ChangeClient theClient = getClientSafely(clientName); return theClient.isDirty(); } /** * Given a client name, return the client, or * throw an exception if client does not exist. * @param clientName * @return ChangeClient object that is associated with the given name. * @throws NotBoundException */ private static ChangeClient getClientSafely (String clientName) throws NotBoundException { ChangeClient theClient = _clients.get(clientName); if (theClient == null) throw new NotBoundException("No client is registered under name '" + clientName + "'"); return theClient; } /**************************************************** * Main and/or Testing Methods *****************************************************/ public static void main (String[] args) { JFrame frame = new JFrame("Test Title"); frame.setVisible(true); try { ChangeManager.registerClient("My client", frame); } catch (AlreadyBoundException e) { } try { System.out.println("ChangeClient dirty?: " + ChangeManager.isDirty ("My client")); System.out.println("Set dirty: " + ChangeManager.markDirty ("My client")); System.out.println("ChangeClient dirty?: " + ChangeManager.isDirty ("My client")); System.out.println("Set clean: " + ChangeManager.markClean ("My client")); System.out.println("ChangeClient dirty?: " + ChangeManager.isDirty ("My client")); } catch (Exception e) { Misc.showErrorMsgAndStackTrace(e, ""); //e.printStackTrace(); } try { JFrame otherFrame = new JFrame(); ChangeManager.registerClient("My client", otherFrame); } catch (AlreadyBoundException e) { System.out.println("Good exception catch"); } frame.dispose(); } }