User config in _ext_mpw, global config.json & residence config.
Moved user preferences (default type & hide passwords) into _ext_mpw. Fixed an issue with JSON serialization of any values. Made update check & background residency globally configurable preferences saved in config.json.
This commit is contained in:
		@@ -147,7 +147,7 @@ public class MPMasterKey {
 | 
				
			|||||||
     * @return {@code null} if the result type is missing a required parameter.
 | 
					     * @return {@code null} if the result type is missing a required parameter.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @throws MPKeyUnavailableException {@link #invalidate()} has been called on this object.
 | 
					     * @throws MPKeyUnavailableException {@link #invalidate()} has been called on this object.
 | 
				
			||||||
     * @throws MPAlgorithmException An internal system or algorithm error has occurred.
 | 
					     * @throws MPAlgorithmException      An internal system or algorithm error has occurred.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    @Nullable
 | 
					    @Nullable
 | 
				
			||||||
    public String siteResult(final String siteName, final MPAlgorithm algorithm, final UnsignedInteger siteCounter,
 | 
					    public String siteResult(final String siteName, final MPAlgorithm algorithm, final UnsignedInteger siteCounter,
 | 
				
			||||||
@@ -185,7 +185,7 @@ public class MPMasterKey {
 | 
				
			|||||||
     *                    {@link #siteResult(String, MPAlgorithm, UnsignedInteger, MPKeyPurpose, String, MPResultType, String)}.
 | 
					     *                    {@link #siteResult(String, MPAlgorithm, UnsignedInteger, MPKeyPurpose, String, MPResultType, String)}.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @throws MPKeyUnavailableException {@link #invalidate()} has been called on this object.
 | 
					     * @throws MPKeyUnavailableException {@link #invalidate()} has been called on this object.
 | 
				
			||||||
     * @throws MPAlgorithmException An internal system or algorithm error has occurred.
 | 
					     * @throws MPAlgorithmException      An internal system or algorithm error has occurred.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    @Nonnull
 | 
					    @Nonnull
 | 
				
			||||||
    public String siteState(final String siteName, final MPAlgorithm algorithm, final UnsignedInteger siteCounter,
 | 
					    public String siteState(final String siteName, final MPAlgorithm algorithm, final UnsignedInteger siteCounter,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,7 @@
 | 
				
			|||||||
package com.lyndir.masterpassword.gui;
 | 
					package com.lyndir.masterpassword.gui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
 | 
					import com.lyndir.lhunath.opal.system.util.ConversionUtils;
 | 
				
			||||||
 | 
					import com.lyndir.masterpassword.model.MPConfig;
 | 
				
			||||||
import com.lyndir.masterpassword.model.MPModelConstants;
 | 
					import com.lyndir.masterpassword.model.MPModelConstants;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -26,15 +27,32 @@ import com.lyndir.masterpassword.model.MPModelConstants;
 | 
				
			|||||||
 * @author lhunath, 2014-08-31
 | 
					 * @author lhunath, 2014-08-31
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@SuppressWarnings("CallToSystemGetenv")
 | 
					@SuppressWarnings("CallToSystemGetenv")
 | 
				
			||||||
public class MPConfig {
 | 
					public class MPGuiConfig extends MPConfig {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static final MPConfig instance = new MPConfig();
 | 
					    public static MPGuiConfig get() {
 | 
				
			||||||
 | 
					        return get( MPGuiConfig.class );
 | 
				
			||||||
    public static MPConfig get() {
 | 
					 | 
				
			||||||
        return instance;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Boolean checkForUpdates;
 | 
				
			||||||
 | 
					    Boolean stayResident;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public boolean checkForUpdates() {
 | 
					    public boolean checkForUpdates() {
 | 
				
			||||||
        return ConversionUtils.toBoolean( System.getenv( MPModelConstants.env_checkUpdates ) ).orElse( true );
 | 
					        return (checkForUpdates != null)? checkForUpdates:
 | 
				
			||||||
 | 
					                ConversionUtils.toBoolean( System.getenv( MPModelConstants.env_checkUpdates ) ).orElse( true );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setCheckForUpdates(final boolean checkForUpdates) {
 | 
				
			||||||
 | 
					        this.checkForUpdates = checkForUpdates;
 | 
				
			||||||
 | 
					        MasterPassword.get().updateCheck();
 | 
				
			||||||
 | 
					        setChanged();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean stayResident() {
 | 
				
			||||||
 | 
					        return (stayResident != null)? stayResident: false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setStayResident(final boolean stayResident) {
 | 
				
			||||||
 | 
					        this.stayResident = stayResident;
 | 
				
			||||||
 | 
					        setChanged();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -46,10 +46,10 @@ import javax.swing.*;
 | 
				
			|||||||
public final class MasterPassword {
 | 
					public final class MasterPassword {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @SuppressWarnings("UnusedDeclaration")
 | 
					    @SuppressWarnings("UnusedDeclaration")
 | 
				
			||||||
    private static final Logger logger = Logger.get( MasterPassword.class );
 | 
					    private static final Logger         logger   = Logger.get( MasterPassword.class );
 | 
				
			||||||
 | 
					 | 
				
			||||||
    private static final MasterPassword instance = new MasterPassword();
 | 
					    private static final MasterPassword instance = new MasterPassword();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final Provider             keyMaster = Provider.getCurrentProvider( true );
 | 
				
			||||||
    private final Collection<Listener> listeners = new CopyOnWriteArraySet<>();
 | 
					    private final Collection<Listener> listeners = new CopyOnWriteArraySet<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Nullable
 | 
					    @Nullable
 | 
				
			||||||
@@ -97,7 +97,29 @@ public final class MasterPassword {
 | 
				
			|||||||
        } );
 | 
					        } );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void checkUpdate() {
 | 
					    public static void main(final String... args) {
 | 
				
			||||||
 | 
					        //Thread.setDefaultUncaughtExceptionHandler(
 | 
				
			||||||
 | 
					        //        (t, e) -> logger.bug( e, "Uncaught: %s", e.getLocalizedMessage() ) );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Set the system look & feel, if available.
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (final UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException ignored) {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Create and open the UI.
 | 
				
			||||||
 | 
					        get().open();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // UI features.
 | 
				
			||||||
 | 
					        get().updateResidence();
 | 
				
			||||||
 | 
					        get().updateCheck();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void updateCheck() {
 | 
				
			||||||
 | 
					        if (!MPGuiConfig.get().checkForUpdates())
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            String implementationVersion = version();
 | 
					            String implementationVersion = version();
 | 
				
			||||||
            String latestVersion = new ByteSource() {
 | 
					            String latestVersion = new ByteSource() {
 | 
				
			||||||
@@ -127,26 +149,10 @@ public final class MasterPassword {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static void main(final String... args) {
 | 
					    public void updateResidence() {
 | 
				
			||||||
        //Thread.setDefaultUncaughtExceptionHandler(
 | 
					        Platform.get().installAppForegroundHandler( get()::open );
 | 
				
			||||||
        //        (t, e) -> logger.bug( e, "Uncaught: %s", e.getLocalizedMessage() ) );
 | 
					        Platform.get().installAppReopenHandler( get()::open );
 | 
				
			||||||
 | 
					        keyMaster.register( MPGuiConstants.ui_hotkey, hotKey -> get().open() );
 | 
				
			||||||
        // Try and set the system look & feel, if available.
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
 | 
					 | 
				
			||||||
            Platform.get().installAppForegroundHandler( get()::open );
 | 
					 | 
				
			||||||
            Platform.get().installAppReopenHandler( get()::open );
 | 
					 | 
				
			||||||
            Provider.getCurrentProvider( true ).register( MPGuiConstants.ui_hotkey, hotKey -> get().open() );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        catch (final UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException ignored) {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Create a platform-specific GUI and open it.
 | 
					 | 
				
			||||||
        get().open();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Check online to see if this version has been superseded.
 | 
					 | 
				
			||||||
        if (MPConfig.get().checkForUpdates())
 | 
					 | 
				
			||||||
            get().checkUpdate();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @SuppressWarnings("InterfaceMayBeAnnotatedFunctional")
 | 
					    @SuppressWarnings("InterfaceMayBeAnnotatedFunctional")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,24 +17,47 @@ public class ApplePlatform implements IPlatform {
 | 
				
			|||||||
    private static final Application application = Preconditions.checkNotNull(
 | 
					    private static final Application application = Preconditions.checkNotNull(
 | 
				
			||||||
            Application.getApplication(), "Not an Apple Java application." );
 | 
					            Application.getApplication(), "Not an Apple Java application." );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private AppForegroundListener appForegroundHandler;
 | 
				
			||||||
 | 
					    private AppReOpenedListener appReopenHandler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public boolean installAppForegroundHandler(final Runnable handler) {
 | 
					    public boolean installAppForegroundHandler(final Runnable handler) {
 | 
				
			||||||
        application.addAppEventListener( new AppForegroundListener() {
 | 
					        if (appForegroundHandler == null)
 | 
				
			||||||
            @Override
 | 
					            application.addAppEventListener( appForegroundHandler = new AppForegroundListener() {
 | 
				
			||||||
            public void appMovedToBackground(final AppEvent.AppForegroundEvent e) {
 | 
					                @Override
 | 
				
			||||||
            }
 | 
					                public void appMovedToBackground(final AppEvent.AppForegroundEvent e) {
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            @Override
 | 
					                @Override
 | 
				
			||||||
            public void appRaisedToForeground(final AppEvent.AppForegroundEvent e) {
 | 
					                public void appRaisedToForeground(final AppEvent.AppForegroundEvent e) {
 | 
				
			||||||
                handler.run();
 | 
					                    handler.run();
 | 
				
			||||||
            }
 | 
					                }
 | 
				
			||||||
        } );
 | 
					            } );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean removeAppForegroundHandler() {
 | 
				
			||||||
 | 
					        if (appForegroundHandler == null)
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        application.removeAppEventListener( appForegroundHandler );
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public boolean installAppReopenHandler(final Runnable handler) {
 | 
					    public boolean installAppReopenHandler(final Runnable handler) {
 | 
				
			||||||
        application.addAppEventListener( (AppReOpenedListener) e -> handler.run() );
 | 
					        application.addAppEventListener( appReopenHandler = e -> handler.run() );
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean removeAppReopenHandler() {
 | 
				
			||||||
 | 
					        if (appReopenHandler == null)
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        application.removeAppEventListener( appReopenHandler );
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,11 +14,21 @@ public class BasePlatform implements IPlatform {
 | 
				
			|||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean removeAppForegroundHandler() {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public boolean installAppReopenHandler(final Runnable handler) {
 | 
					    public boolean installAppReopenHandler(final Runnable handler) {
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean removeAppReopenHandler() {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public boolean requestForeground() {
 | 
					    public boolean requestForeground() {
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,8 +12,12 @@ public interface IPlatform {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    boolean installAppForegroundHandler(Runnable handler);
 | 
					    boolean installAppForegroundHandler(Runnable handler);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    boolean removeAppForegroundHandler();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    boolean installAppReopenHandler(Runnable handler);
 | 
					    boolean installAppReopenHandler(Runnable handler);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    boolean removeAppReopenHandler();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    boolean requestForeground();
 | 
					    boolean requestForeground();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    boolean show(File file);
 | 
					    boolean show(File file);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,24 +17,49 @@ public class JDK9Platform implements IPlatform {
 | 
				
			|||||||
    private static final Logger  logger  = Logger.get( JDK9Platform.class );
 | 
					    private static final Logger  logger  = Logger.get( JDK9Platform.class );
 | 
				
			||||||
    private static final Desktop desktop = Desktop.getDesktop();
 | 
					    private static final Desktop desktop = Desktop.getDesktop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private AppForegroundListener appForegroundHandler;
 | 
				
			||||||
 | 
					    private AppReopenedListener   appReopenHandler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public boolean installAppForegroundHandler(final Runnable handler) {
 | 
					    public boolean installAppForegroundHandler(final Runnable handler) {
 | 
				
			||||||
        desktop.addAppEventListener( new AppForegroundListener() {
 | 
					        if (appForegroundHandler == null)
 | 
				
			||||||
            @Override
 | 
					            desktop.addAppEventListener( appForegroundHandler = new AppForegroundListener() {
 | 
				
			||||||
            public void appRaisedToForeground(final AppForegroundEvent e) {
 | 
					                @Override
 | 
				
			||||||
                handler.run();
 | 
					                public void appRaisedToForeground(final AppForegroundEvent e) {
 | 
				
			||||||
            }
 | 
					                    handler.run();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            @Override
 | 
					                @Override
 | 
				
			||||||
            public void appMovedToBackground(final AppForegroundEvent e) {
 | 
					                public void appMovedToBackground(final AppForegroundEvent e) {
 | 
				
			||||||
            }
 | 
					                }
 | 
				
			||||||
        } );
 | 
					            } );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean removeAppForegroundHandler() {
 | 
				
			||||||
 | 
					        if (appForegroundHandler == null)
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        desktop.removeAppEventListener( appForegroundHandler );
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public boolean installAppReopenHandler(final Runnable handler) {
 | 
					    public boolean installAppReopenHandler(final Runnable handler) {
 | 
				
			||||||
        desktop.addAppEventListener( (AppReopenedListener) e -> handler.run() );
 | 
					        if (appReopenHandler == null)
 | 
				
			||||||
 | 
					            desktop.addAppEventListener( appReopenHandler = e -> handler.run() );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean removeAppReopenHandler() {
 | 
				
			||||||
 | 
					        if (appReopenHandler == null)
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        desktop.removeAppEventListener( appReopenHandler );
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,12 @@
 | 
				
			|||||||
package com.lyndir.masterpassword.gui.view;
 | 
					package com.lyndir.masterpassword.gui.view;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
					import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
				
			||||||
 | 
					import com.lyndir.masterpassword.gui.MPGuiConfig;
 | 
				
			||||||
import com.lyndir.masterpassword.gui.util.Components;
 | 
					import com.lyndir.masterpassword.gui.util.Components;
 | 
				
			||||||
import com.lyndir.masterpassword.gui.util.Res;
 | 
					import com.lyndir.masterpassword.gui.util.Res;
 | 
				
			||||||
import com.lyndir.masterpassword.model.impl.MPFileUserManager;
 | 
					import com.lyndir.masterpassword.model.impl.MPFileUserManager;
 | 
				
			||||||
import java.awt.*;
 | 
					import java.awt.*;
 | 
				
			||||||
import java.awt.event.ComponentAdapter;
 | 
					import java.awt.event.*;
 | 
				
			||||||
import java.awt.event.ComponentEvent;
 | 
					 | 
				
			||||||
import javax.swing.*;
 | 
					import javax.swing.*;
 | 
				
			||||||
import javax.swing.border.BevelBorder;
 | 
					import javax.swing.border.BevelBorder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -19,7 +19,7 @@ public class MasterPasswordFrame extends JFrame {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private static final Logger logger = Logger.get( MasterPasswordFrame.class );
 | 
					    private static final Logger logger = Logger.get( MasterPasswordFrame.class );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private final UserContentPanel         userContent;
 | 
					    private final UserContentPanel userContent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @SuppressWarnings("MagicNumber")
 | 
					    @SuppressWarnings("MagicNumber")
 | 
				
			||||||
    public MasterPasswordFrame() {
 | 
					    public MasterPasswordFrame() {
 | 
				
			||||||
@@ -39,6 +39,7 @@ public class MasterPasswordFrame extends JFrame {
 | 
				
			|||||||
        userPanel.add( userContent.getSiteToolbar(), BorderLayout.LINE_END );
 | 
					        userPanel.add( userContent.getSiteToolbar(), BorderLayout.LINE_END );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        addComponentListener( new ComponentHandler() );
 | 
					        addComponentListener( new ComponentHandler() );
 | 
				
			||||||
 | 
					        addWindowListener( new WindowHandler() );
 | 
				
			||||||
        setPreferredSize( new Dimension( 800, 560 ) );
 | 
					        setPreferredSize( new Dimension( 800, 560 ) );
 | 
				
			||||||
        setDefaultCloseOperation( DISPOSE_ON_CLOSE );
 | 
					        setDefaultCloseOperation( DISPOSE_ON_CLOSE );
 | 
				
			||||||
        pack();
 | 
					        pack();
 | 
				
			||||||
@@ -55,4 +56,14 @@ public class MasterPasswordFrame extends JFrame {
 | 
				
			|||||||
            userContent.transferFocus();
 | 
					            userContent.transferFocus();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static class WindowHandler extends WindowAdapter {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void windowClosed(final WindowEvent e) {
 | 
				
			||||||
 | 
					            if (!MPGuiConfig.get().stayResident())
 | 
				
			||||||
 | 
					                System.exit( 0 );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,8 +9,7 @@ import com.google.common.util.concurrent.ListenableFuture;
 | 
				
			|||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
					import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
				
			||||||
import com.lyndir.lhunath.opal.system.util.ObjectUtils;
 | 
					import com.lyndir.lhunath.opal.system.util.ObjectUtils;
 | 
				
			||||||
import com.lyndir.masterpassword.*;
 | 
					import com.lyndir.masterpassword.*;
 | 
				
			||||||
import com.lyndir.masterpassword.gui.MPGuiConstants;
 | 
					import com.lyndir.masterpassword.gui.*;
 | 
				
			||||||
import com.lyndir.masterpassword.gui.MasterPassword;
 | 
					 | 
				
			||||||
import com.lyndir.masterpassword.gui.model.*;
 | 
					import com.lyndir.masterpassword.gui.model.*;
 | 
				
			||||||
import com.lyndir.masterpassword.gui.util.*;
 | 
					import com.lyndir.masterpassword.gui.util.*;
 | 
				
			||||||
import com.lyndir.masterpassword.gui.util.Platform;
 | 
					import com.lyndir.masterpassword.gui.util.Platform;
 | 
				
			||||||
@@ -577,21 +576,25 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            components.add( Components.label( "Default Algorithm:" ),
 | 
					            components.add( Components.label( "Default Algorithm:" ),
 | 
				
			||||||
                            Components.comboBox( MPAlgorithm.Version.values(), MPAlgorithm.Version::name,
 | 
					                            Components.comboBox( MPAlgorithm.Version.values(), MPAlgorithm.Version::name,
 | 
				
			||||||
                                                 user.getAlgorithm().version(),
 | 
					                                                 user.getAlgorithm().version(), version -> user.setAlgorithm( version.getAlgorithm() ) ) );
 | 
				
			||||||
                                                 version -> user.setAlgorithm( version.getAlgorithm() ) ) );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            MPFileUser fileUser = (user instanceof MPFileUser)? (MPFileUser) user: null;
 | 
					            components.add( Components.label( "Default Password Type:" ),
 | 
				
			||||||
            if (fileUser != null) {
 | 
					                            Components.comboBox( MPResultType.values(), MPResultType::getLongName,
 | 
				
			||||||
                components.add( Components.label( "Default Password Type:" ),
 | 
					                                                 user.getPreferences().getDefaultType(), user.getPreferences()::setDefaultType ),
 | 
				
			||||||
                                Components.comboBox( MPResultType.values(), MPResultType::getLongName,
 | 
					                            Components.strut() );
 | 
				
			||||||
                                                     fileUser.getPreferences().getDefaultType(),
 | 
					 | 
				
			||||||
                                                     fileUser.getPreferences()::setDefaultType ),
 | 
					 | 
				
			||||||
                                Components.strut() );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                components.add( Components.checkBox( "Hide Passwords",
 | 
					            components.add( Components.checkBox( "Hide Passwords",
 | 
				
			||||||
                                                     fileUser.getPreferences().isHidePasswords(),
 | 
					                                                 user.getPreferences().isHidePasswords(), user.getPreferences()::setHidePasswords ) );
 | 
				
			||||||
                                                     fileUser.getPreferences()::setHidePasswords ) );
 | 
					
 | 
				
			||||||
            }
 | 
					            components.add( new JSeparator() );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            components.add( Components.checkBox( "Check For Updates",
 | 
				
			||||||
 | 
					                                                 MPGuiConfig.get().checkForUpdates(), MPGuiConfig.get()::setCheckForUpdates ) );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            components.add( Components.checkBox( strf( "<html>Stay Resident (reactivate with <strong><code>%s+%s</code></strong>)",
 | 
				
			||||||
 | 
					                                                       InputEvent.getModifiersExText( MPGuiConstants.ui_hotkey.getModifiers() ),
 | 
				
			||||||
 | 
					                                                       KeyEvent.getKeyText( MPGuiConstants.ui_hotkey.getKeyCode() ) ),
 | 
				
			||||||
 | 
					                                                 MPGuiConfig.get().stayResident(), MPGuiConfig.get()::setStayResident ) );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Components.showDialog( this, user.getFullName(), new JOptionPane( Components.panel(
 | 
					            Components.showDialog( this, user.getFullName(), new JOptionPane( Components.panel(
 | 
				
			||||||
                    BoxLayout.PAGE_AXIS, components.build().toArray( new Component[0] ) ) ) );
 | 
					                    BoxLayout.PAGE_AXIS, components.build().toArray( new Component[0] ) ) ) );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,78 @@
 | 
				
			|||||||
 | 
					package com.lyndir.masterpassword.model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.fasterxml.jackson.annotation.JsonIgnore;
 | 
				
			||||||
 | 
					import com.google.common.collect.ClassToInstanceMap;
 | 
				
			||||||
 | 
					import com.google.common.collect.MutableClassToInstanceMap;
 | 
				
			||||||
 | 
					import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
				
			||||||
 | 
					import com.lyndir.masterpassword.model.impl.Changeable;
 | 
				
			||||||
 | 
					import com.lyndir.masterpassword.model.impl.MPJSONAnyObject;
 | 
				
			||||||
 | 
					import java.io.File;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @author lhunath, 2018-10-14
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@SuppressWarnings("CallToSystemGetenv")
 | 
				
			||||||
 | 
					public class MPConfig extends MPJSONAnyObject {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final Logger                       logger     = Logger.get( MPConfig.class );
 | 
				
			||||||
 | 
					    private static final ClassToInstanceMap<MPConfig> instances  = MutableClassToInstanceMap.create();
 | 
				
			||||||
 | 
					    private static final File                         configFile = new File( rcDir(), "config.json" );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final        Changeable                   changeable = new Changeable() {
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        protected void onChanged() {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                objectMapper.writerWithDefaultPrettyPrinter().writeValue( configFile, MPConfig.this );
 | 
				
			||||||
 | 
					                instances.clear();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (final IOException e) {
 | 
				
			||||||
 | 
					                logger.err( e, "While saving config to: %s", configFile );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected static synchronized <C extends MPConfig> C get(final Class<C> type) {
 | 
				
			||||||
 | 
					        C instance = instances.getInstance( type );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (instance == null)
 | 
				
			||||||
 | 
					            if (configFile.exists())
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    instances.putInstance( type, instance = objectMapper.readValue( configFile, type ) );
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                catch (final IOException e) {
 | 
				
			||||||
 | 
					                    logger.wrn( e, "While reading config file: %s", configFile );
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (instance == null)
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                instance = type.getConstructor().newInstance();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (final ReflectiveOperationException e) {
 | 
				
			||||||
 | 
					                throw logger.bug( e );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return instance;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected void setChanged() {
 | 
				
			||||||
 | 
					        changeable.setChanged();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static MPConfig get() {
 | 
				
			||||||
 | 
					        return get( MPConfig.class );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static File rcDir() {
 | 
				
			||||||
 | 
					        String rcDir = System.getenv( MPModelConstants.env_rcDir );
 | 
				
			||||||
 | 
					        if (rcDir != null)
 | 
				
			||||||
 | 
					            return new File( rcDir );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        String home = System.getProperty( "user.home" );
 | 
				
			||||||
 | 
					        if (home == null)
 | 
				
			||||||
 | 
					            home = System.getenv( "HOME" );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return new File( home, ".mpw.d" );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -40,14 +40,14 @@ public interface MPQuestion extends Comparable<MPQuestion> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    void setType(MPResultType type);
 | 
					    void setType(MPResultType type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Nonnull
 | 
					    @Nullable
 | 
				
			||||||
    default String getAnswer()
 | 
					    default String getAnswer()
 | 
				
			||||||
            throws MPKeyUnavailableException, MPAlgorithmException {
 | 
					            throws MPKeyUnavailableException, MPAlgorithmException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return getAnswer( null );
 | 
					        return getAnswer( null );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Nonnull
 | 
					    @Nullable
 | 
				
			||||||
    String getAnswer(@Nullable String state)
 | 
					    String getAnswer(@Nullable String state)
 | 
				
			||||||
            throws MPKeyUnavailableException, MPAlgorithmException;
 | 
					            throws MPKeyUnavailableException, MPAlgorithmException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ import java.util.concurrent.Executors;
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * @author lhunath, 2018-07-08
 | 
					 * @author lhunath, 2018-07-08
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class Changeable {
 | 
					public abstract class Changeable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static final ExecutorService changeExecutor = Executors.newSingleThreadExecutor();
 | 
					    private static final ExecutorService changeExecutor = Executors.newSingleThreadExecutor();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -15,7 +15,9 @@ public class Changeable {
 | 
				
			|||||||
    private       Grouping grouping = Grouping.APPLY;
 | 
					    private       Grouping grouping = Grouping.APPLY;
 | 
				
			||||||
    private       boolean  changed;
 | 
					    private       boolean  changed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void setChanged() {
 | 
					    protected abstract void onChanged();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setChanged() {
 | 
				
			||||||
        synchronized (mutex) {
 | 
					        synchronized (mutex) {
 | 
				
			||||||
            if (changed)
 | 
					            if (changed)
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
@@ -37,9 +39,6 @@ public class Changeable {
 | 
				
			|||||||
        } );
 | 
					        } );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected void onChanged() {
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public void beginChanges() {
 | 
					    public void beginChanges() {
 | 
				
			||||||
        synchronized (mutex) {
 | 
					        synchronized (mutex) {
 | 
				
			||||||
            grouping = Grouping.BATCH;
 | 
					            grouping = Grouping.BATCH;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -72,7 +72,7 @@ public abstract class MPBasicQuestion extends Changeable implements MPQuestion {
 | 
				
			|||||||
        setChanged();
 | 
					        setChanged();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Nonnull
 | 
					    @Nullable
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public String getAnswer(@Nullable final String state)
 | 
					    public String getAnswer(@Nullable final String state)
 | 
				
			||||||
            throws MPKeyUnavailableException, MPAlgorithmException {
 | 
					            throws MPKeyUnavailableException, MPAlgorithmException {
 | 
				
			||||||
@@ -82,8 +82,6 @@ public abstract class MPBasicQuestion extends Changeable implements MPQuestion {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    protected void onChanged() {
 | 
					    protected void onChanged() {
 | 
				
			||||||
        super.onChanged();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (site instanceof Changeable)
 | 
					        if (site instanceof Changeable)
 | 
				
			||||||
            ((Changeable) site).setChanged();
 | 
					            ((Changeable) site).setChanged();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -201,8 +201,6 @@ public abstract class MPBasicSite<U extends MPUser<?>, Q extends MPQuestion> ext
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    protected void onChanged() {
 | 
					    protected void onChanged() {
 | 
				
			||||||
        super.onChanged();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (user instanceof Changeable)
 | 
					        if (user instanceof Changeable)
 | 
				
			||||||
            ((Changeable) user).setChanged();
 | 
					            ((Changeable) user).setChanged();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -214,8 +214,6 @@ public abstract class MPBasicUser<S extends MPBasicSite<?, ?>> extends Changeabl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    protected void onChanged() {
 | 
					    protected void onChanged() {
 | 
				
			||||||
        super.onChanged();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (final Listener listener : listeners)
 | 
					        for (final Listener listener : listeners)
 | 
				
			||||||
            listener.onUserUpdated( this );
 | 
					            listener.onUserUpdated( this );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,6 @@ package com.lyndir.masterpassword.model.impl;
 | 
				
			|||||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
 | 
					import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.lyndir.masterpassword.*;
 | 
					import com.lyndir.masterpassword.*;
 | 
				
			||||||
import javax.annotation.Nonnull;
 | 
					 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					import javax.annotation.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -33,6 +32,7 @@ public class MPFileQuestion extends MPBasicQuestion {
 | 
				
			|||||||
    @Nullable
 | 
					    @Nullable
 | 
				
			||||||
    private String answerState;
 | 
					    private String answerState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @SuppressWarnings("TypeMayBeWeakened")
 | 
				
			||||||
    public MPFileQuestion(final MPFileSite site, final String keyword,
 | 
					    public MPFileQuestion(final MPFileSite site, final String keyword,
 | 
				
			||||||
                          @Nullable final MPResultType type, @Nullable final String answerState) {
 | 
					                          @Nullable final MPResultType type, @Nullable final String answerState) {
 | 
				
			||||||
        super( site, keyword, ifNotNullElse( type, site.getAlgorithm().mpw_default_answer_type() ) );
 | 
					        super( site, keyword, ifNotNullElse( type, site.getAlgorithm().mpw_default_answer_type() ) );
 | 
				
			||||||
@@ -45,7 +45,7 @@ public class MPFileQuestion extends MPBasicQuestion {
 | 
				
			|||||||
        return answerState;
 | 
					        return answerState;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Nonnull
 | 
					    @Nullable
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public String getAnswer()
 | 
					    public String getAnswer()
 | 
				
			||||||
            throws MPKeyUnavailableException, MPAlgorithmException {
 | 
					            throws MPKeyUnavailableException, MPAlgorithmException {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,11 +18,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.lyndir.masterpassword.model.impl;
 | 
					package com.lyndir.masterpassword.model.impl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.google.common.collect.ImmutableSortedSet;
 | 
					import com.google.common.collect.ImmutableSortedSet;
 | 
				
			||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
					import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
				
			||||||
import com.lyndir.masterpassword.model.MPModelConstants;
 | 
					import com.lyndir.masterpassword.model.MPConfig;
 | 
				
			||||||
import java.io.File;
 | 
					import java.io.File;
 | 
				
			||||||
import java.io.IOException;
 | 
					import java.io.IOException;
 | 
				
			||||||
import java.util.*;
 | 
					import java.util.*;
 | 
				
			||||||
@@ -38,19 +36,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
 | 
				
			|||||||
public class MPFileUserManager {
 | 
					public class MPFileUserManager {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @SuppressWarnings("UnusedDeclaration")
 | 
					    @SuppressWarnings("UnusedDeclaration")
 | 
				
			||||||
    private static final Logger            logger = Logger.get( MPFileUserManager.class );
 | 
					    private static final Logger            logger   = Logger.get( MPFileUserManager.class );
 | 
				
			||||||
    private static final MPFileUserManager instance;
 | 
					    private static final MPFileUserManager instance = create( MPConfig.get().rcDir() );
 | 
				
			||||||
 | 
					 | 
				
			||||||
    static {
 | 
					 | 
				
			||||||
        String rcDir = System.getenv( MPModelConstants.env_rcDir );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (rcDir != null)
 | 
					 | 
				
			||||||
            instance = create( new File( rcDir ) );
 | 
					 | 
				
			||||||
        else {
 | 
					 | 
				
			||||||
            String home = ifNotNullElseNullable( System.getProperty( "user.home" ), System.getenv( "HOME" ) );
 | 
					 | 
				
			||||||
            instance = create( new File( home, ".mpw.d" ) );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private final Collection<Listener>    listeners  = new CopyOnWriteArraySet<>();
 | 
					    private final Collection<Listener>    listeners  = new CopyOnWriteArraySet<>();
 | 
				
			||||||
    private final Map<String, MPFileUser> userByName = new HashMap<>();
 | 
					    private final Map<String, MPFileUser> userByName = new HashMap<>();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,9 @@
 | 
				
			|||||||
package com.lyndir.masterpassword.model.impl;
 | 
					package com.lyndir.masterpassword.model.impl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.fasterxml.jackson.annotation.*;
 | 
					import com.fasterxml.jackson.annotation.*;
 | 
				
			||||||
 | 
					import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
 | 
				
			||||||
 | 
					import com.fasterxml.jackson.core.util.Separators;
 | 
				
			||||||
 | 
					import com.fasterxml.jackson.databind.ObjectMapper;
 | 
				
			||||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 | 
					import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 | 
				
			||||||
import java.util.*;
 | 
					import java.util.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -27,31 +30,59 @@ import java.util.*;
 | 
				
			|||||||
 * @author lhunath, 2018-05-14
 | 
					 * @author lhunath, 2018-05-14
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = MPJSONAnyObject.MPJSONEmptyValue.class)
 | 
					@JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = MPJSONAnyObject.MPJSONEmptyValue.class)
 | 
				
			||||||
class MPJSONAnyObject {
 | 
					public class MPJSONAnyObject {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @SuppressWarnings("serial")
 | 
				
			||||||
 | 
					    protected static final ObjectMapper objectMapper = new ObjectMapper() {
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            setDefaultPrettyPrinter( new DefaultPrettyPrinter() {
 | 
				
			||||||
 | 
					                @Override
 | 
				
			||||||
 | 
					                public DefaultPrettyPrinter withSeparators(final Separators separators) {
 | 
				
			||||||
 | 
					                    super.withSeparators( separators );
 | 
				
			||||||
 | 
					                    _objectFieldValueSeparatorWithSpaces = separators.getObjectFieldValueSeparator() + " ";
 | 
				
			||||||
 | 
					                    return this;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } );
 | 
				
			||||||
 | 
					            setVisibility( PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE );
 | 
				
			||||||
 | 
					            setVisibility( PropertyAccessor.FIELD, JsonAutoDetect.Visibility.NON_PRIVATE );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @JsonAnySetter
 | 
					    @JsonAnySetter
 | 
				
			||||||
    final Map<String, Object> any = new LinkedHashMap<>();
 | 
					    final Map<String, Object> any = new LinkedHashMap<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @JsonAnyGetter
 | 
					    @JsonAnyGetter
 | 
				
			||||||
    public Map<String, Object> getAny() {
 | 
					    public Map<String, Object> any() {
 | 
				
			||||||
        return Collections.unmodifiableMap( any );
 | 
					        return Collections.unmodifiableMap( any );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @SuppressWarnings("unchecked")
 | 
				
			||||||
 | 
					    public <V> V any(final String key) {
 | 
				
			||||||
 | 
					        return (V) any.get( key );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @SuppressWarnings("EqualsAndHashcode")
 | 
					    @SuppressWarnings("EqualsAndHashcode")
 | 
				
			||||||
    public static class MPJSONEmptyValue {
 | 
					    public static class MPJSONEmptyValue {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @Override
 | 
					        @Override
 | 
				
			||||||
        @SuppressWarnings({ "ChainOfInstanceofChecks", "Contract" })
 | 
					        @SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
 | 
				
			||||||
        @SuppressFBWarnings({ "EQ_UNUSUAL", "EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS", "HE_EQUALS_USE_HASHCODE" })
 | 
					        @SuppressFBWarnings({ "EQ_UNUSUAL", "EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS", "HE_EQUALS_USE_HASHCODE" })
 | 
				
			||||||
        public boolean equals(final Object obj) {
 | 
					        public boolean equals(final Object obj) {
 | 
				
			||||||
 | 
					            return isEmpty( obj );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @SuppressWarnings({ "ChainOfInstanceofChecks", "ConstantConditions" })
 | 
				
			||||||
 | 
					        private static boolean isEmpty(final Object obj) {
 | 
				
			||||||
 | 
					            if (obj == null)
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
            if (obj instanceof Collection<?>)
 | 
					            if (obj instanceof Collection<?>)
 | 
				
			||||||
                return ((Collection<?>) obj).isEmpty();
 | 
					                return ((Collection<?>) obj).isEmpty();
 | 
				
			||||||
            if (obj instanceof Map<?, ?>)
 | 
					            if (obj instanceof Map<?, ?>)
 | 
				
			||||||
                return ((Map<?, ?>) obj).isEmpty();
 | 
					                return ((Map<?, ?>) obj).isEmpty();
 | 
				
			||||||
            if (obj instanceof MPJSONFile.Site.Ext)
 | 
					            if (obj instanceof MPJSONAnyObject)
 | 
				
			||||||
                return ((MPJSONAnyObject) obj).any.isEmpty();
 | 
					                return ((MPJSONAnyObject) obj).any.isEmpty() && (objectMapper.valueToTree( obj ).size() == 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return obj == null;
 | 
					            return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,22 +44,6 @@ import org.joda.time.Instant;
 | 
				
			|||||||
@SuppressFBWarnings("URF_UNREAD_FIELD")
 | 
					@SuppressFBWarnings("URF_UNREAD_FIELD")
 | 
				
			||||||
public class MPJSONFile extends MPJSONAnyObject {
 | 
					public class MPJSONFile extends MPJSONAnyObject {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected static final ObjectMapper objectMapper = new ObjectMapper();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    static {
 | 
					 | 
				
			||||||
        objectMapper.setDefaultPrettyPrinter( new DefaultPrettyPrinter() {
 | 
					 | 
				
			||||||
            private static final long serialVersionUID = 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            @Override
 | 
					 | 
				
			||||||
            public DefaultPrettyPrinter withSeparators(final Separators separators) {
 | 
					 | 
				
			||||||
                super.withSeparators( separators );
 | 
					 | 
				
			||||||
                _objectFieldValueSeparatorWithSpaces = separators.getObjectFieldValueSeparator() + " ";
 | 
					 | 
				
			||||||
                return this;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } );
 | 
					 | 
				
			||||||
        objectMapper.setVisibility( PropertyAccessor.FIELD, JsonAutoDetect.Visibility.NON_PRIVATE );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    MPJSONFile() {
 | 
					    MPJSONFile() {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -79,8 +63,12 @@ public class MPJSONFile extends MPJSONAnyObject {
 | 
				
			|||||||
        user.last_used = MPModelConstants.dateTimeFormatter.print( modelUser.getLastUsed() );
 | 
					        user.last_used = MPModelConstants.dateTimeFormatter.print( modelUser.getLastUsed() );
 | 
				
			||||||
        user.key_id = modelUser.exportKeyID();
 | 
					        user.key_id = modelUser.exportKeyID();
 | 
				
			||||||
        user.algorithm = modelUser.getAlgorithm().version();
 | 
					        user.algorithm = modelUser.getAlgorithm().version();
 | 
				
			||||||
        user.default_type = modelUser.getPreferences().getDefaultType();
 | 
					        user._ext_mpw = new User.Ext() {
 | 
				
			||||||
        user.hide_passwords = modelUser.getPreferences().isHidePasswords();
 | 
					            {
 | 
				
			||||||
 | 
					                default_type = modelUser.getPreferences().getDefaultType();
 | 
				
			||||||
 | 
					                hide_passwords = modelUser.getPreferences().isHidePasswords();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Section "sites"
 | 
					        // Section "sites"
 | 
				
			||||||
        sites = new LinkedHashMap<>();
 | 
					        sites = new LinkedHashMap<>();
 | 
				
			||||||
@@ -131,8 +119,11 @@ public class MPJSONFile extends MPJSONAnyObject {
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                } );
 | 
					                } );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            site._ext_mpw = new Site.Ext();
 | 
					            site._ext_mpw = new Site.Ext() {
 | 
				
			||||||
            site._ext_mpw.url = modelSite.getUrl();
 | 
					                {
 | 
				
			||||||
 | 
					                    url = modelSite.getUrl();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -141,9 +132,10 @@ public class MPJSONFile extends MPJSONAnyObject {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return new MPFileUser(
 | 
					        return new MPFileUser(
 | 
				
			||||||
                user.full_name, CodeUtils.decodeHex( user.key_id ), algorithm, user.avatar,
 | 
					                user.full_name, CodeUtils.decodeHex( user.key_id ), algorithm, user.avatar,
 | 
				
			||||||
                (user.default_type != null)? user.default_type: algorithm.mpw_default_result_type(),
 | 
					                (user._ext_mpw != null)? user._ext_mpw.default_type: null,
 | 
				
			||||||
                (user.last_used != null)? MPModelConstants.dateTimeFormatter.parseDateTime( user.last_used ): new Instant(),
 | 
					                (user.last_used != null)? MPModelConstants.dateTimeFormatter.parseDateTime( user.last_used ): new Instant(),
 | 
				
			||||||
                user.hide_passwords, export.redacted? MPMarshaller.ContentMode.PROTECTED: MPMarshaller.ContentMode.VISIBLE,
 | 
					                (user._ext_mpw != null) && user._ext_mpw.hide_passwords,
 | 
				
			||||||
 | 
					                export.redacted? MPMarshaller.ContentMode.PROTECTED: MPMarshaller.ContentMode.VISIBLE,
 | 
				
			||||||
                MPMarshalFormat.JSON, file
 | 
					                MPMarshalFormat.JSON, file
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -203,16 +195,24 @@ public class MPJSONFile extends MPJSONAnyObject {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public static class User extends MPJSONAnyObject {
 | 
					    public static class User extends MPJSONAnyObject {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        int     avatar;
 | 
					        int    avatar;
 | 
				
			||||||
        String  full_name;
 | 
					        String full_name;
 | 
				
			||||||
        String  last_used;
 | 
					        String last_used;
 | 
				
			||||||
        boolean hide_passwords;
 | 
					 | 
				
			||||||
        @Nullable
 | 
					        @Nullable
 | 
				
			||||||
        String              key_id;
 | 
					        String              key_id;
 | 
				
			||||||
        @Nullable
 | 
					        @Nullable
 | 
				
			||||||
        MPAlgorithm.Version algorithm;
 | 
					        MPAlgorithm.Version algorithm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @Nullable
 | 
					        @Nullable
 | 
				
			||||||
        MPResultType        default_type;
 | 
					        Ext _ext_mpw;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static class Ext extends MPJSONAnyObject {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            @Nullable
 | 
				
			||||||
 | 
					            MPResultType default_type;
 | 
				
			||||||
 | 
					            boolean hide_passwords;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user