diff --git a/platform-independent/java/algorithm/src/main/java/com/lyndir/masterpassword/MPMasterKey.java b/platform-independent/java/algorithm/src/main/java/com/lyndir/masterpassword/MPMasterKey.java index 8d4fde49..4465be88 100644 --- a/platform-independent/java/algorithm/src/main/java/com/lyndir/masterpassword/MPMasterKey.java +++ b/platform-independent/java/algorithm/src/main/java/com/lyndir/masterpassword/MPMasterKey.java @@ -147,7 +147,7 @@ public class MPMasterKey { * @return {@code null} if the result type is missing a required parameter. * * @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 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)}. * * @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 public String siteState(final String siteName, final MPAlgorithm algorithm, final UnsignedInteger siteCounter, diff --git a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/MPConfig.java b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/MPGuiConfig.java similarity index 59% rename from platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/MPConfig.java rename to platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/MPGuiConfig.java index ca1daa7d..b58c5a86 100644 --- a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/MPConfig.java +++ b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/MPGuiConfig.java @@ -19,6 +19,7 @@ package com.lyndir.masterpassword.gui; import com.lyndir.lhunath.opal.system.util.ConversionUtils; +import com.lyndir.masterpassword.model.MPConfig; import com.lyndir.masterpassword.model.MPModelConstants; @@ -26,15 +27,32 @@ import com.lyndir.masterpassword.model.MPModelConstants; * @author lhunath, 2014-08-31 */ @SuppressWarnings("CallToSystemGetenv") -public class MPConfig { +public class MPGuiConfig extends MPConfig { - private static final MPConfig instance = new MPConfig(); - - public static MPConfig get() { - return instance; + public static MPGuiConfig get() { + return get( MPGuiConfig.class ); } + Boolean checkForUpdates; + Boolean stayResident; + 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(); } } diff --git a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/MasterPassword.java b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/MasterPassword.java index f64c181e..8d31651c 100644 --- a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/MasterPassword.java +++ b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/MasterPassword.java @@ -46,10 +46,10 @@ import javax.swing.*; public final class MasterPassword { @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 final Provider keyMaster = Provider.getCurrentProvider( true ); private final Collection listeners = new CopyOnWriteArraySet<>(); @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 { String implementationVersion = version(); String latestVersion = new ByteSource() { @@ -127,26 +149,10 @@ public final class MasterPassword { } } - public static void main(final String... args) { - //Thread.setDefaultUncaughtExceptionHandler( - // (t, e) -> logger.bug( e, "Uncaught: %s", e.getLocalizedMessage() ) ); - - // 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(); + public void updateResidence() { + Platform.get().installAppForegroundHandler( get()::open ); + Platform.get().installAppReopenHandler( get()::open ); + keyMaster.register( MPGuiConstants.ui_hotkey, hotKey -> get().open() ); } @SuppressWarnings("InterfaceMayBeAnnotatedFunctional") diff --git a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/platform/ApplePlatform.java b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/platform/ApplePlatform.java index 04da5931..acfb458f 100644 --- a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/platform/ApplePlatform.java +++ b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/platform/ApplePlatform.java @@ -17,24 +17,47 @@ public class ApplePlatform implements IPlatform { private static final Application application = Preconditions.checkNotNull( Application.getApplication(), "Not an Apple Java application." ); + private AppForegroundListener appForegroundHandler; + private AppReOpenedListener appReopenHandler; + @Override public boolean installAppForegroundHandler(final Runnable handler) { - application.addAppEventListener( new AppForegroundListener() { - @Override - public void appMovedToBackground(final AppEvent.AppForegroundEvent e) { - } + if (appForegroundHandler == null) + application.addAppEventListener( appForegroundHandler = new AppForegroundListener() { + @Override + public void appMovedToBackground(final AppEvent.AppForegroundEvent e) { + } - @Override - public void appRaisedToForeground(final AppEvent.AppForegroundEvent e) { - handler.run(); - } - } ); + @Override + public void appRaisedToForeground(final AppEvent.AppForegroundEvent e) { + handler.run(); + } + } ); + + return true; + } + + @Override + public boolean removeAppForegroundHandler() { + if (appForegroundHandler == null) + return false; + + application.removeAppEventListener( appForegroundHandler ); return true; } @Override 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; } diff --git a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/platform/BasePlatform.java b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/platform/BasePlatform.java index 38884f40..e15b73db 100644 --- a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/platform/BasePlatform.java +++ b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/platform/BasePlatform.java @@ -14,11 +14,21 @@ public class BasePlatform implements IPlatform { return false; } + @Override + public boolean removeAppForegroundHandler() { + return false; + } + @Override public boolean installAppReopenHandler(final Runnable handler) { return false; } + @Override + public boolean removeAppReopenHandler() { + return false; + } + @Override public boolean requestForeground() { return false; diff --git a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/platform/IPlatform.java b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/platform/IPlatform.java index a568d9fb..762ae85f 100644 --- a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/platform/IPlatform.java +++ b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/platform/IPlatform.java @@ -12,8 +12,12 @@ public interface IPlatform { boolean installAppForegroundHandler(Runnable handler); + boolean removeAppForegroundHandler(); + boolean installAppReopenHandler(Runnable handler); + boolean removeAppReopenHandler(); + boolean requestForeground(); boolean show(File file); diff --git a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/platform/JDK9Platform.java b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/platform/JDK9Platform.java index b11a4f2f..6bb0407b 100644 --- a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/platform/JDK9Platform.java +++ b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/platform/JDK9Platform.java @@ -17,24 +17,49 @@ public class JDK9Platform implements IPlatform { private static final Logger logger = Logger.get( JDK9Platform.class ); private static final Desktop desktop = Desktop.getDesktop(); + private AppForegroundListener appForegroundHandler; + private AppReopenedListener appReopenHandler; + @Override public boolean installAppForegroundHandler(final Runnable handler) { - desktop.addAppEventListener( new AppForegroundListener() { - @Override - public void appRaisedToForeground(final AppForegroundEvent e) { - handler.run(); - } + if (appForegroundHandler == null) + desktop.addAppEventListener( appForegroundHandler = new AppForegroundListener() { + @Override + public void appRaisedToForeground(final AppForegroundEvent e) { + handler.run(); + } - @Override - public void appMovedToBackground(final AppForegroundEvent e) { - } - } ); + @Override + public void appMovedToBackground(final AppForegroundEvent e) { + } + } ); + + return true; + } + + @Override + public boolean removeAppForegroundHandler() { + if (appForegroundHandler == null) + return false; + + desktop.removeAppEventListener( appForegroundHandler ); return true; } @Override 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; } diff --git a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/MasterPasswordFrame.java b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/MasterPasswordFrame.java index 05e2bf1e..50cc607d 100644 --- a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/MasterPasswordFrame.java +++ b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/MasterPasswordFrame.java @@ -1,12 +1,12 @@ package com.lyndir.masterpassword.gui.view; 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.Res; import com.lyndir.masterpassword.model.impl.MPFileUserManager; import java.awt.*; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; +import java.awt.event.*; import javax.swing.*; import javax.swing.border.BevelBorder; @@ -19,7 +19,7 @@ public class MasterPasswordFrame extends JFrame { private static final Logger logger = Logger.get( MasterPasswordFrame.class ); - private final UserContentPanel userContent; + private final UserContentPanel userContent; @SuppressWarnings("MagicNumber") public MasterPasswordFrame() { @@ -39,6 +39,7 @@ public class MasterPasswordFrame extends JFrame { userPanel.add( userContent.getSiteToolbar(), BorderLayout.LINE_END ); addComponentListener( new ComponentHandler() ); + addWindowListener( new WindowHandler() ); setPreferredSize( new Dimension( 800, 560 ) ); setDefaultCloseOperation( DISPOSE_ON_CLOSE ); pack(); @@ -55,4 +56,14 @@ public class MasterPasswordFrame extends JFrame { userContent.transferFocus(); } } + + + private static class WindowHandler extends WindowAdapter { + + @Override + public void windowClosed(final WindowEvent e) { + if (!MPGuiConfig.get().stayResident()) + System.exit( 0 ); + } + } } diff --git a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/UserContentPanel.java b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/UserContentPanel.java index 7ef280c7..21714b15 100644 --- a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/UserContentPanel.java +++ b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/UserContentPanel.java @@ -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.util.ObjectUtils; import com.lyndir.masterpassword.*; -import com.lyndir.masterpassword.gui.MPGuiConstants; -import com.lyndir.masterpassword.gui.MasterPassword; +import com.lyndir.masterpassword.gui.*; import com.lyndir.masterpassword.gui.model.*; import com.lyndir.masterpassword.gui.util.*; 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.comboBox( MPAlgorithm.Version.values(), MPAlgorithm.Version::name, - user.getAlgorithm().version(), - version -> user.setAlgorithm( version.getAlgorithm() ) ) ); + user.getAlgorithm().version(), version -> user.setAlgorithm( version.getAlgorithm() ) ) ); - MPFileUser fileUser = (user instanceof MPFileUser)? (MPFileUser) user: null; - if (fileUser != null) { - components.add( Components.label( "Default Password Type:" ), - Components.comboBox( MPResultType.values(), MPResultType::getLongName, - fileUser.getPreferences().getDefaultType(), - fileUser.getPreferences()::setDefaultType ), - Components.strut() ); + components.add( Components.label( "Default Password Type:" ), + Components.comboBox( MPResultType.values(), MPResultType::getLongName, + user.getPreferences().getDefaultType(), user.getPreferences()::setDefaultType ), + Components.strut() ); - components.add( Components.checkBox( "Hide Passwords", - fileUser.getPreferences().isHidePasswords(), - fileUser.getPreferences()::setHidePasswords ) ); - } + components.add( Components.checkBox( "Hide Passwords", + user.getPreferences().isHidePasswords(), user.getPreferences()::setHidePasswords ) ); + + components.add( new JSeparator() ); + + components.add( Components.checkBox( "Check For Updates", + MPGuiConfig.get().checkForUpdates(), MPGuiConfig.get()::setCheckForUpdates ) ); + + components.add( Components.checkBox( strf( "Stay Resident (reactivate with %s+%s)", + 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( BoxLayout.PAGE_AXIS, components.build().toArray( new Component[0] ) ) ) ); diff --git a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/MPConfig.java b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/MPConfig.java new file mode 100644 index 00000000..f78c701e --- /dev/null +++ b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/MPConfig.java @@ -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 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 get(final Class 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" ); + } +} diff --git a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/MPQuestion.java b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/MPQuestion.java index 29687f54..fbd3c11d 100644 --- a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/MPQuestion.java +++ b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/MPQuestion.java @@ -40,14 +40,14 @@ public interface MPQuestion extends Comparable { void setType(MPResultType type); - @Nonnull + @Nullable default String getAnswer() throws MPKeyUnavailableException, MPAlgorithmException { return getAnswer( null ); } - @Nonnull + @Nullable String getAnswer(@Nullable String state) throws MPKeyUnavailableException, MPAlgorithmException; diff --git a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/Changeable.java b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/Changeable.java index b21b2610..2d3a0246 100644 --- a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/Changeable.java +++ b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/Changeable.java @@ -7,7 +7,7 @@ import java.util.concurrent.Executors; /** * @author lhunath, 2018-07-08 */ -public class Changeable { +public abstract class Changeable { private static final ExecutorService changeExecutor = Executors.newSingleThreadExecutor(); @@ -15,7 +15,9 @@ public class Changeable { private Grouping grouping = Grouping.APPLY; private boolean changed; - void setChanged() { + protected abstract void onChanged(); + + public void setChanged() { synchronized (mutex) { if (changed) return; @@ -37,9 +39,6 @@ public class Changeable { } ); } - protected void onChanged() { - } - public void beginChanges() { synchronized (mutex) { grouping = Grouping.BATCH; diff --git a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicQuestion.java b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicQuestion.java index 454715fb..8c7fdb7b 100644 --- a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicQuestion.java +++ b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicQuestion.java @@ -72,7 +72,7 @@ public abstract class MPBasicQuestion extends Changeable implements MPQuestion { setChanged(); } - @Nonnull + @Nullable @Override public String getAnswer(@Nullable final String state) throws MPKeyUnavailableException, MPAlgorithmException { @@ -82,8 +82,6 @@ public abstract class MPBasicQuestion extends Changeable implements MPQuestion { @Override protected void onChanged() { - super.onChanged(); - if (site instanceof Changeable) ((Changeable) site).setChanged(); } diff --git a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicSite.java b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicSite.java index 3fbf823d..9b646d15 100644 --- a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicSite.java +++ b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicSite.java @@ -201,8 +201,6 @@ public abstract class MPBasicSite, Q extends MPQuestion> ext @Override protected void onChanged() { - super.onChanged(); - if (user instanceof Changeable) ((Changeable) user).setChanged(); } diff --git a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicUser.java b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicUser.java index 86f181ab..7df83c6e 100755 --- a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicUser.java +++ b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicUser.java @@ -214,8 +214,6 @@ public abstract class MPBasicUser> extends Changeabl @Override protected void onChanged() { - super.onChanged(); - for (final Listener listener : listeners) listener.onUserUpdated( this ); } diff --git a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileQuestion.java b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileQuestion.java index 05817c30..6ad3e6bf 100644 --- a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileQuestion.java +++ b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileQuestion.java @@ -21,7 +21,6 @@ package com.lyndir.masterpassword.model.impl; import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*; import com.lyndir.masterpassword.*; -import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -33,6 +32,7 @@ public class MPFileQuestion extends MPBasicQuestion { @Nullable private String answerState; + @SuppressWarnings("TypeMayBeWeakened") public MPFileQuestion(final MPFileSite site, final String keyword, @Nullable final MPResultType type, @Nullable final String answerState) { super( site, keyword, ifNotNullElse( type, site.getAlgorithm().mpw_default_answer_type() ) ); @@ -45,7 +45,7 @@ public class MPFileQuestion extends MPBasicQuestion { return answerState; } - @Nonnull + @Nullable @Override public String getAnswer() throws MPKeyUnavailableException, MPAlgorithmException { diff --git a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUserManager.java b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUserManager.java index d7b9dda6..800a0637 100644 --- a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUserManager.java +++ b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUserManager.java @@ -18,11 +18,9 @@ package com.lyndir.masterpassword.model.impl; -import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*; - import com.google.common.collect.ImmutableSortedSet; 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.IOException; import java.util.*; @@ -38,19 +36,8 @@ import java.util.concurrent.CopyOnWriteArraySet; public class MPFileUserManager { @SuppressWarnings("UnusedDeclaration") - private static final Logger logger = Logger.get( MPFileUserManager.class ); - private static final MPFileUserManager instance; - - 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 static final Logger logger = Logger.get( MPFileUserManager.class ); + private static final MPFileUserManager instance = create( MPConfig.get().rcDir() ); private final Collection listeners = new CopyOnWriteArraySet<>(); private final Map userByName = new HashMap<>(); diff --git a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONAnyObject.java b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONAnyObject.java index 1726bf59..e86437e7 100644 --- a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONAnyObject.java +++ b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONAnyObject.java @@ -19,6 +19,9 @@ package com.lyndir.masterpassword.model.impl; 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 java.util.*; @@ -27,31 +30,59 @@ import java.util.*; * @author lhunath, 2018-05-14 */ @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 final Map any = new LinkedHashMap<>(); @JsonAnyGetter - public Map getAny() { + public Map any() { return Collections.unmodifiableMap( any ); } + @SuppressWarnings("unchecked") + public V any(final String key) { + return (V) any.get( key ); + } + @SuppressWarnings("EqualsAndHashcode") public static class MPJSONEmptyValue { @Override - @SuppressWarnings({ "ChainOfInstanceofChecks", "Contract" }) + @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") @SuppressFBWarnings({ "EQ_UNUSUAL", "EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS", "HE_EQUALS_USE_HASHCODE" }) 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) return ((Collection) obj).isEmpty(); if (obj instanceof Map) return ((Map) obj).isEmpty(); - if (obj instanceof MPJSONFile.Site.Ext) - return ((MPJSONAnyObject) obj).any.isEmpty(); + if (obj instanceof MPJSONAnyObject) + return ((MPJSONAnyObject) obj).any.isEmpty() && (objectMapper.valueToTree( obj ).size() == 0); - return obj == null; + return false; } } } diff --git a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONFile.java b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONFile.java index 5b64350e..5cb3b85a 100644 --- a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONFile.java +++ b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONFile.java @@ -44,22 +44,6 @@ import org.joda.time.Instant; @SuppressFBWarnings("URF_UNREAD_FIELD") 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() { } @@ -79,8 +63,12 @@ public class MPJSONFile extends MPJSONAnyObject { user.last_used = MPModelConstants.dateTimeFormatter.print( modelUser.getLastUsed() ); user.key_id = modelUser.exportKeyID(); user.algorithm = modelUser.getAlgorithm().version(); - user.default_type = modelUser.getPreferences().getDefaultType(); - user.hide_passwords = modelUser.getPreferences().isHidePasswords(); + user._ext_mpw = new User.Ext() { + { + default_type = modelUser.getPreferences().getDefaultType(); + hide_passwords = modelUser.getPreferences().isHidePasswords(); + } + }; // Section "sites" sites = new LinkedHashMap<>(); @@ -131,8 +119,11 @@ public class MPJSONFile extends MPJSONAnyObject { } } ); - site._ext_mpw = new Site.Ext(); - site._ext_mpw.url = modelSite.getUrl(); + site._ext_mpw = new Site.Ext() { + { + url = modelSite.getUrl(); + } + }; } } @@ -141,9 +132,10 @@ public class MPJSONFile extends MPJSONAnyObject { return new MPFileUser( 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.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 ); } @@ -203,16 +195,24 @@ public class MPJSONFile extends MPJSONAnyObject { public static class User extends MPJSONAnyObject { - int avatar; - String full_name; - String last_used; - boolean hide_passwords; + int avatar; + String full_name; + String last_used; @Nullable String key_id; @Nullable MPAlgorithm.Version algorithm; + @Nullable - MPResultType default_type; + Ext _ext_mpw; + + + public static class Ext extends MPJSONAnyObject { + + @Nullable + MPResultType default_type; + boolean hide_passwords; + } }